Bug 739679 - Part 3: BasicTiledThebesLayer. r=mwoodrow,roc

This commit is contained in:
Benoit Girard 2012-04-16 19:02:45 -04:00
parent 757a6fa342
commit 9a37018a8c
4 changed files with 457 additions and 4 deletions

View File

@ -72,6 +72,7 @@ EXPORTS = \
CPPSRCS = \
BasicImages.cpp \
BasicLayers.cpp \
BasicTiledThebesLayer.cpp \
Layers.cpp \
RenderTrace.cpp \
ReadbackProcessor.cpp \

View File

@ -49,6 +49,7 @@
#include "BasicLayers.h"
#include "BasicImplData.h"
#include "BasicTiledThebesLayer.h"
#include "ImageLayers.h"
#include "RenderTrace.h"
@ -3126,10 +3127,23 @@ already_AddRefed<ThebesLayer>
BasicShadowLayerManager::CreateThebesLayer()
{
NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
nsRefPtr<BasicShadowableThebesLayer> layer =
new BasicShadowableThebesLayer(this);
MAYBE_CREATE_SHADOW(Thebes);
return layer.forget();
#ifdef FORCE_BASICTILEDTHEBESLAYER
if (HasShadowManager()) {
// BasicTiledThebesLayer doesn't support main
// thread compositing so only return this layer
// type if we have a shadow manager.
nsRefPtr<BasicTiledThebesLayer> layer =
new BasicTiledThebesLayer(this);
MAYBE_CREATE_SHADOW(Thebes);
return layer.forget();
} else
#endif
{
nsRefPtr<BasicShadowableThebesLayer> layer =
new BasicShadowableThebesLayer(this);
MAYBE_CREATE_SHADOW(Thebes);
return layer.forget();
}
}
already_AddRefed<ContainerLayer>

View File

@ -0,0 +1,227 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/layers/PLayersChild.h"
#include "BasicTiledThebesLayer.h"
#include "gfxImageSurface.h"
#include "sampler.h"
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
#include "cairo.h"
#include <sstream>
using mozilla::layers::Layer;
static void DrawDebugOverlay(gfxImageSurface* imgSurf, int x, int y)
{
gfxContext c(imgSurf);
// Draw border
c.NewPath();
c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0));
c.Rectangle(gfxRect(gfxPoint(0,0),imgSurf->GetSize()));
c.Stroke();
// Build tile description
std::stringstream ss;
ss << x << ", " << y;
// Draw text using cairo toy text API
cairo_t* cr = c.GetCairo();
cairo_set_font_size(cr, 10);
cairo_text_extents_t extents;
cairo_text_extents(cr, ss.str().c_str(), &extents);
int textWidth = extents.width + 6;
c.NewPath();
c.SetDeviceColor(gfxRGBA(0.0, 0.0, 0.0, 1.0));
c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 15)));
c.Fill();
c.NewPath();
c.SetDeviceColor(gfxRGBA(1.0, 0.0, 0.0, 1.0));
c.Rectangle(gfxRect(gfxPoint(2,2),gfxSize(textWidth, 15)));
c.Stroke();
c.NewPath();
cairo_move_to(cr, 4, 13);
cairo_show_text(cr, ss.str().c_str());
}
#endif
namespace mozilla {
namespace layers {
void
BasicTiledLayerBuffer::PaintThebes(BasicTiledThebesLayer* aLayer,
const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{
mThebesLayer = aLayer;
mCallback = aCallback;
mCallbackData = aCallbackData;
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
long start = PR_IntervalNow();
#endif
if (UseSinglePaintBuffer()) {
SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesSingleBuffer");
const nsIntRect bounds = aPaintRegion.GetBounds();
mSinglePaintBuffer = new gfxImageSurface(gfxIntSize(bounds.width, bounds.height), gfxASurface::ImageFormatRGB16_565);
mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
nsRefPtr<gfxContext> ctxt = new gfxContext(mSinglePaintBuffer);
ctxt->NewPath();
ctxt->Translate(gfxPoint(-bounds.x, -bounds.y));
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 3) {
printf_stderr("Slow alloc %i\n", PR_IntervalNow() - start);
}
start = PR_IntervalNow();
#endif
mCallback(mThebesLayer, ctxt, aPaintRegion, aPaintRegion, mCallbackData);
}
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 30) {
const nsIntRect bounds = aPaintRegion.GetBounds();
printf_stderr("Time to draw %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
if (aPaintRegion.IsComplex()) {
printf_stderr("Complex region\n");
nsIntRegionRectIterator it(aPaintRegion);
for (const nsIntRect* rect = it.Next(); rect != nsnull; rect = it.Next()) {
printf_stderr(" rect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height);
}
}
}
start = PR_IntervalNow();
#endif
SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesUpdate");
Update(aNewValidRegion, aPaintRegion);
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (PR_IntervalNow() - start > 10) {
const nsIntRect bounds = aPaintRegion.GetBounds();
printf_stderr("Time to tile %i: %i, %i, %i, %i\n", PR_IntervalNow() - start, bounds.x, bounds.y, bounds.width, bounds.height);
}
#endif
mThebesLayer = nsnull;
mCallback = nsnull;
mCallbackData = nsnull;
mSinglePaintBuffer = nsnull;
}
BasicTiledLayerTile
BasicTiledLayerBuffer::ValidateTileInternal(BasicTiledLayerTile aTile,
const nsIntPoint& aTileOrigin,
const nsIntRect& aDirtyRect)
{
if (aTile == GetPlaceholderTile()) {
gfxImageSurface* tmpTile = new gfxImageSurface(gfxIntSize(GetTileLength(), GetTileLength()), gfxASurface::ImageFormatRGB16_565);
aTile = BasicTiledLayerTile(tmpTile);
}
gfxRect drawRect(aDirtyRect.x - aTileOrigin.x, aDirtyRect.y - aTileOrigin.y,
aDirtyRect.width, aDirtyRect.height);
// Use the gfxReusableSurfaceWrapper, which will reuse the surface
// if the compositor no longer has a read lock, otherwise the surface
// will be copied into a new writable surface.
gfxImageSurface* writableSurface;
aTile.mSurface = aTile.mSurface->GetWritable(&writableSurface);
// Bug 742100, this gfxContext really should live on the stack.
nsRefPtr<gfxContext> ctxt = new gfxContext(writableSurface);
ctxt->NewPath();
ctxt->SetOperator(gfxContext::OPERATOR_CLEAR);
ctxt->Rectangle(drawRect, true);
ctxt->Fill();
ctxt->SetOperator(gfxContext::OPERATOR_OVER);
if (mSinglePaintBuffer) {
ctxt->NewPath();
ctxt->SetSource(mSinglePaintBuffer.get(),
gfxPoint(mSinglePaintBufferOffset.x - aDirtyRect.x + drawRect.x,
mSinglePaintBufferOffset.y - aDirtyRect.y + drawRect.y));
ctxt->Rectangle(drawRect, true);
ctxt->Fill();
} else {
ctxt->NewPath();
ctxt->Translate(gfxPoint(-aTileOrigin.x, -aTileOrigin.y));
nsIntPoint a = aTileOrigin;
mCallback(mThebesLayer, ctxt, nsIntRegion(nsIntRect(a, nsIntSize(GetTileLength(), GetTileLength()))), aDirtyRect, mCallbackData);
}
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
DrawDebugOverlay(writableSurface, aTileOrigin.x, aTileOrigin.y);
//aTile->DumpAsDataURL();
#endif
return aTile;
}
BasicTiledLayerTile
BasicTiledLayerBuffer::ValidateTile(BasicTiledLayerTile aTile,
const nsIntPoint& aTileOrigin,
const nsIntRegion& aDirtyRegion)
{
SAMPLE_LABEL("BasicTiledLayerBuffer", "ValidateTile");
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
if (aDirtyRegion.IsComplex()) {
printf_stderr("Complex region\n");
}
#endif
nsIntRegionRectIterator it(aDirtyRegion);
for (const nsIntRect* rect = it.Next(); rect != nsnull; rect = it.Next()) {
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
printf_stderr(" break into subrect %i, %i, %i, %i\n", rect->x, rect->y, rect->width, rect->height);
#endif
aTile = ValidateTileInternal(aTile, aTileOrigin, *rect);
}
return aTile;
}
void
BasicTiledThebesLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
{
aAttrs = ThebesLayerAttributes(GetValidRegion());
}
void
BasicTiledThebesLayer::PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback)
{
if (!aCallback) {
BasicManager()->SetTransactionIncomplete();
return;
}
if (!HasShadow()) {
NS_ASSERTION(false, "Shadow requested for painting\n");
return;
}
nsIntRegion regionToPaint = mVisibleRegion;
regionToPaint.Sub(regionToPaint, mValidRegion);
if (regionToPaint.IsEmpty())
return;
mTiledBuffer.PaintThebes(this, mVisibleRegion, regionToPaint, aCallback, aCallbackData);
mTiledBuffer.ReadLock();
mValidRegion = mVisibleRegion;
BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), &mTiledBuffer);
}
} // mozilla
} // layers

View File

@ -0,0 +1,211 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GFX_BASICTILEDTHEBESLAYER_H
#define GFX_BASICTILEDTHEBESLAYER_H
#include "TiledLayerBuffer.h"
#include "gfxReusableSurfaceWrapper.h"
#include "mozilla/layers/ShadowLayers.h"
#include "BasicLayers.h"
#include "BasicImplData.h"
#include <algorithm>
namespace mozilla {
namespace layers {
/**
* Represent a single tile in tiled buffer. It's backed
* by a gfxReusableSurfaceWrapper that implements a
* copy-on-write mechanism while locked. The tile should be
* locked before being sent to the compositor and unlocked
* as soon as it is uploaded to prevent a copy.
* Ideal place to store per tile debug information.
*/
struct BasicTiledLayerTile {
nsRefPtr<gfxReusableSurfaceWrapper> mSurface;
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
TimeStamp mLastUpdate;
#endif
// Placeholder
BasicTiledLayerTile()
: mSurface(NULL)
{}
explicit BasicTiledLayerTile(gfxImageSurface* aSurface)
: mSurface(new gfxReusableSurfaceWrapper(aSurface))
{
}
BasicTiledLayerTile(const BasicTiledLayerTile& o) {
mSurface = o.mSurface;
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
mLastUpdate = o.mLastUpdate;
#endif
}
BasicTiledLayerTile& operator=(const BasicTiledLayerTile& o) {
if (this == &o) return *this;
mSurface = o.mSurface;
#ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
mLastUpdate = o.mLastUpdate;
#endif
return *this;
}
bool operator== (const BasicTiledLayerTile& o) const {
return mSurface == o.mSurface;
}
bool operator!= (const BasicTiledLayerTile& o) const {
return mSurface != o.mSurface;
}
void ReadUnlock() {
mSurface->ReadUnlock();
}
void ReadLock() {
mSurface->ReadLock();
}
};
class BasicTiledThebesLayer;
/**
* Provide an instance of TiledLayerBuffer backed by image surfaces.
* This buffer provides an implementation to ValidateTile using a
* thebes callback and can support painting using a single paint buffer
* which is much faster then painting directly into the tiles.
*/
class BasicTiledLayerBuffer : public TiledLayerBuffer<BasicTiledLayerBuffer, BasicTiledLayerTile>
{
friend class TiledLayerBuffer<BasicTiledLayerBuffer, BasicTiledLayerTile>;
public:
void PaintThebes(BasicTiledThebesLayer* aLayer,
const nsIntRegion& aNewValidRegion,
const nsIntRegion& aPaintRegion,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData);
BasicTiledLayerTile GetPlaceholderTile() const {
return mPlaceholder;
}
void ReadUnlock() {
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
if (mRetainedTiles[i] == GetPlaceholderTile()) continue;
mRetainedTiles[i].ReadUnlock();
}
}
void ReadLock() {
for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
if (mRetainedTiles[i] == GetPlaceholderTile()) continue;
mRetainedTiles[i].ReadLock();
}
}
protected:
BasicTiledLayerTile ValidateTile(BasicTiledLayerTile aTile,
const nsIntPoint& aTileRect,
const nsIntRegion& dirtyRect);
// If this returns true, we perform the paint operation into a single large
// buffer and copy it out to the tiles instead of calling PaintThebes() on
// each tile individually. Somewhat surprisingly, this turns out to be faster
// on Android.
bool UseSinglePaintBuffer() { return true; }
void ReleaseTile(BasicTiledLayerTile aTile) { /* No-op. */ }
void SwapTiles(BasicTiledLayerTile& aTileA, BasicTiledLayerTile& aTileB) {
std::swap(aTileA, aTileB);
}
private:
BasicTiledThebesLayer* mThebesLayer;
LayerManager::DrawThebesLayerCallback mCallback;
void* mCallbackData;
// The buffer we use when UseSinglePaintBuffer() above is true.
nsRefPtr<gfxImageSurface> mSinglePaintBuffer;
nsIntPoint mSinglePaintBufferOffset;
BasicTiledLayerTile mPlaceholder;
BasicTiledLayerTile ValidateTileInternal(BasicTiledLayerTile aTile,
const nsIntPoint& aTileOrigin,
const nsIntRect& aDirtyRect);
};
/**
* An implementation of ThebesLayer that ONLY supports remote
* composition that is backed by tiles. This thebes layer implementation
* is better suited to mobile hardware to work around slow implementation
* of glTexImage2D (for OGL compositors), and restrait memory bandwidth.
*/
class BasicTiledThebesLayer : public ThebesLayer,
public BasicImplData,
public BasicShadowableLayer
{
typedef ThebesLayer Base;
public:
BasicTiledThebesLayer(BasicShadowLayerManager* const aManager)
: ThebesLayer(aManager, static_cast<BasicImplData*>(this))
{
MOZ_COUNT_CTOR(BasicTiledThebesLayer);
}
~BasicTiledThebesLayer()
{
MOZ_COUNT_DTOR(BasicTiledThebesLayer);
}
// Thebes Layer
virtual Layer* AsLayer() { return this; }
virtual void InvalidateRegion(const nsIntRegion& aRegion) {
mValidRegion.Sub(mValidRegion, aRegion);
}
// BasicImplData
virtual bool MustRetainContent() { return HasShadow(); }
// Shadow methods
virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
virtual ShadowableLayer* AsShadowableLayer() { return this; }
virtual void Disconnect()
{
BasicShadowableLayer::Disconnect();
}
virtual void PaintThebes(gfxContext* aContext,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData,
ReadbackProcessor* aReadback);
private:
BasicShadowLayerManager* BasicManager()
{
return static_cast<BasicShadowLayerManager*>(mManager);
}
// BasicImplData
virtual void
PaintBuffer(gfxContext* aContext,
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aExtendedRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
bool aDidSelfCopy,
LayerManager::DrawThebesLayerCallback aCallback,
void* aCallbackData)
{ NS_RUNTIMEABORT("Not reached."); }
// Members
BasicTiledLayerBuffer mTiledBuffer;
};
} // layers
} // mozilla
#endif