2012-04-16 16:02:45 -07:00
|
|
|
/* 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"
|
2012-08-19 12:33:25 -07:00
|
|
|
#include "gfxPlatform.h"
|
2012-04-16 16:02:45 -07:00
|
|
|
|
|
|
|
#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 {
|
|
|
|
|
2012-09-04 15:05:31 -07:00
|
|
|
bool
|
|
|
|
BasicTiledLayerBuffer::HasFormatChanged(BasicTiledThebesLayer* aThebesLayer) const
|
|
|
|
{
|
|
|
|
return aThebesLayer->CanUseOpaqueSurface() != mLastPaintOpaque;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-16 16:04:24 -07:00
|
|
|
gfxASurface::gfxImageFormat
|
|
|
|
BasicTiledLayerBuffer::GetFormat() const
|
|
|
|
{
|
|
|
|
if (mThebesLayer->CanUseOpaqueSurface()) {
|
|
|
|
return gfxASurface::ImageFormatRGB16_565;
|
|
|
|
} else {
|
|
|
|
return gfxASurface::ImageFormatARGB32;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-16 16:02:45 -07:00
|
|
|
void
|
|
|
|
BasicTiledLayerBuffer::PaintThebes(BasicTiledThebesLayer* aLayer,
|
|
|
|
const nsIntRegion& aNewValidRegion,
|
|
|
|
const nsIntRegion& aPaintRegion,
|
|
|
|
LayerManager::DrawThebesLayerCallback aCallback,
|
2012-03-18 16:02:38 -07:00
|
|
|
void* aCallbackData)
|
2012-04-16 16:02:45 -07:00
|
|
|
{
|
|
|
|
mThebesLayer = aLayer;
|
|
|
|
mCallback = aCallback;
|
|
|
|
mCallbackData = aCallbackData;
|
|
|
|
|
|
|
|
#ifdef GFX_TILEDLAYER_PREF_WARNINGS
|
|
|
|
long start = PR_IntervalNow();
|
|
|
|
#endif
|
|
|
|
if (UseSinglePaintBuffer()) {
|
|
|
|
const nsIntRect bounds = aPaintRegion.GetBounds();
|
2012-04-18 17:40:08 -07:00
|
|
|
{
|
|
|
|
SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesSingleBufferAlloc");
|
|
|
|
mSinglePaintBuffer = new gfxImageSurface(gfxIntSize(bounds.width, bounds.height), GetFormat(), !aLayer->CanUseOpaqueSurface());
|
|
|
|
mSinglePaintBufferOffset = nsIntPoint(bounds.x, bounds.y);
|
|
|
|
}
|
2012-04-16 16:02:45 -07:00
|
|
|
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
|
2012-04-18 17:40:08 -07:00
|
|
|
SAMPLE_LABEL("BasicTiledLayerBuffer", "PaintThebesSingleBufferDraw");
|
2012-02-07 14:27:21 -08:00
|
|
|
|
2012-05-15 13:34:06 -07:00
|
|
|
mCallback(mThebesLayer, ctxt, aPaintRegion, nsIntRegion(), mCallbackData);
|
2012-04-16 16:02:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#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);
|
2012-07-30 07:20:58 -07:00
|
|
|
for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {
|
2012-04-16 16:02:45 -07:00
|
|
|
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
|
|
|
|
|
2012-09-04 15:05:31 -07:00
|
|
|
mLastPaintOpaque = mThebesLayer->CanUseOpaqueSurface();
|
2012-07-30 07:20:58 -07:00
|
|
|
mThebesLayer = nullptr;
|
|
|
|
mCallback = nullptr;
|
|
|
|
mCallbackData = nullptr;
|
|
|
|
mSinglePaintBuffer = nullptr;
|
2012-04-16 16:02:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
BasicTiledLayerTile
|
|
|
|
BasicTiledLayerBuffer::ValidateTileInternal(BasicTiledLayerTile aTile,
|
|
|
|
const nsIntPoint& aTileOrigin,
|
|
|
|
const nsIntRect& aDirtyRect)
|
|
|
|
{
|
2012-08-30 12:35:36 -07:00
|
|
|
if (aTile == GetPlaceholderTile() || aTile.mSurface->Format() != GetFormat()) {
|
2012-04-16 16:04:24 -07:00
|
|
|
gfxImageSurface* tmpTile = new gfxImageSurface(gfxIntSize(GetTileLength(), GetTileLength()),
|
2012-04-18 17:40:08 -07:00
|
|
|
GetFormat(), !mThebesLayer->CanUseOpaqueSurface());
|
2012-04-16 16:02:45 -07:00
|
|
|
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);
|
2012-05-04 12:35:57 -07:00
|
|
|
ctxt->SetOperator(gfxContext::OPERATOR_SOURCE);
|
2012-04-16 16:02:45 -07:00
|
|
|
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);
|
|
|
|
#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);
|
2012-07-30 07:20:58 -07:00
|
|
|
for (const nsIntRect* rect = it.Next(); rect != nullptr; rect = it.Next()) {
|
2012-04-16 16:02:45 -07:00
|
|
|
#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,
|
2012-07-06 11:02:10 -07:00
|
|
|
Layer* aMaskLayer,
|
2012-04-16 16:02:45 -07:00
|
|
|
LayerManager::DrawThebesLayerCallback aCallback,
|
|
|
|
void* aCallbackData,
|
|
|
|
ReadbackProcessor* aReadback)
|
|
|
|
{
|
|
|
|
if (!aCallback) {
|
|
|
|
BasicManager()->SetTransactionIncomplete();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!HasShadow()) {
|
|
|
|
NS_ASSERTION(false, "Shadow requested for painting\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-09-04 15:05:31 -07:00
|
|
|
if (mTiledBuffer.HasFormatChanged(this)) {
|
|
|
|
mValidRegion = nsIntRegion();
|
|
|
|
}
|
|
|
|
|
2012-04-16 16:02:45 -07:00
|
|
|
nsIntRegion regionToPaint = mVisibleRegion;
|
|
|
|
regionToPaint.Sub(regionToPaint, mValidRegion);
|
|
|
|
if (regionToPaint.IsEmpty())
|
|
|
|
return;
|
|
|
|
|
2012-07-06 11:38:50 -07:00
|
|
|
if (gfxPlatform::UseProgressiveTilePainting()) {
|
2012-07-06 11:02:10 -07:00
|
|
|
nsIntRegionRectIterator it(regionToPaint);
|
|
|
|
const nsIntRect* rect = it.Next();
|
|
|
|
if (!rect)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Currently we start painting from the first rect of the invalid
|
|
|
|
// region and convert that into a tile.
|
|
|
|
// TODO: Use a smart tile prioritization such as:
|
|
|
|
// (1) Paint tiles that have no content first
|
|
|
|
// (2) Then paint tiles that have stale content
|
|
|
|
// (3) Order tiles using they position from relevant
|
|
|
|
// user interaction events.
|
|
|
|
int paintTileStartX = mTiledBuffer.RoundDownToTileEdge(rect->x);
|
|
|
|
int paintTileStartY = mTiledBuffer.RoundDownToTileEdge(rect->y);
|
|
|
|
|
|
|
|
nsIntRegion maxPaint(
|
|
|
|
nsIntRect(paintTileStartX, paintTileStartY,
|
|
|
|
mTiledBuffer.GetTileLength(), mTiledBuffer.GetTileLength()));
|
|
|
|
|
|
|
|
if (!maxPaint.Contains(regionToPaint)) {
|
|
|
|
// The region needed to paint is larger then our progressive chunk size
|
|
|
|
// therefore update what we want to paint and ask for a new paint transaction.
|
|
|
|
regionToPaint.And(regionToPaint, maxPaint);
|
|
|
|
BasicManager()->SetRepeatTransaction();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We want to continue to retain invalidated tiles that we're about to paint soon
|
|
|
|
// to prevent them from disapearing while doing progressive paint. However we only
|
|
|
|
// want to this if they were painted at the same resolution.
|
|
|
|
gfxSize resolution(1, 1);
|
|
|
|
for (ContainerLayer* parent = GetParent(); parent; parent = parent->GetParent()) {
|
|
|
|
const FrameMetrics& metrics = parent->GetFrameMetrics();
|
|
|
|
resolution.width *= metrics.mResolution.width;
|
|
|
|
resolution.height *= metrics.mResolution.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIntRegion regionToRetain(mTiledBuffer.GetValidRegion());
|
|
|
|
if (false && mTiledBuffer.GetResolution() == resolution) {
|
|
|
|
// Retain stale tiles but keep them marked as invalid in mValidRegion
|
|
|
|
// so that they will be eventually repainted.
|
|
|
|
regionToRetain.And(regionToRetain, mVisibleRegion);
|
|
|
|
regionToRetain.Or(regionToRetain, regionToPaint);
|
|
|
|
} else {
|
|
|
|
regionToRetain = mValidRegion;
|
|
|
|
regionToRetain.Or(regionToRetain, regionToPaint);
|
|
|
|
mTiledBuffer.SetResolution(resolution);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Paint and keep track of what we refreshed
|
|
|
|
mTiledBuffer.PaintThebes(this, regionToRetain, regionToPaint, aCallback, aCallbackData);
|
|
|
|
mValidRegion.Or(mValidRegion, regionToPaint);
|
|
|
|
} else {
|
|
|
|
mTiledBuffer.PaintThebes(this, mVisibleRegion, regionToPaint, aCallback, aCallbackData);
|
|
|
|
mValidRegion = mVisibleRegion;
|
|
|
|
}
|
|
|
|
|
2012-04-16 16:02:45 -07:00
|
|
|
mTiledBuffer.ReadLock();
|
2012-03-18 16:02:38 -07:00
|
|
|
if (aMaskLayer) {
|
|
|
|
static_cast<BasicImplData*>(aMaskLayer->ImplData())
|
2012-07-30 07:20:58 -07:00
|
|
|
->Paint(aContext, nullptr);
|
2012-03-18 16:02:38 -07:00
|
|
|
}
|
2012-04-16 16:02:45 -07:00
|
|
|
|
2012-05-01 12:59:41 -07:00
|
|
|
// Create a heap copy owned and released by the compositor. This is needed
|
|
|
|
// since we're sending this over an async message and content needs to be
|
|
|
|
// be able to modify the tiled buffer in the next transaction.
|
|
|
|
// TODO: Remove me once Bug 747811 lands.
|
|
|
|
BasicTiledLayerBuffer *heapCopy = new BasicTiledLayerBuffer(mTiledBuffer);
|
|
|
|
|
|
|
|
BasicManager()->PaintedTiledLayerBuffer(BasicManager()->Hold(this), heapCopy);
|
2012-04-16 16:02:45 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} // mozilla
|
|
|
|
} // layers
|