Bug 564991. Part 2: Create retained layer API: methods on FrameLayerBuilder that frames can use to access, reuse and invalidate retained layers. r=mats,sr=vlad

This commit is contained in:
Robert O'Callahan 2010-07-16 09:07:46 +12:00
parent 4a3fae344e
commit 496811aed4
18 changed files with 182 additions and 44 deletions

View File

@ -104,7 +104,8 @@ public:
// Return the CanvasLayer for this context, creating
// one for the given layer manager if not available.
virtual already_AddRefed<CanvasLayer> GetCanvasLayer(LayerManager *mgr) = 0;
virtual already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager) = 0;
virtual void MarkContextClean() = 0;

View File

@ -79,6 +79,7 @@ WebGLContext::WebGLContext()
mWidth = mHeight = 0;
mGeneration = 0;
mInvalidated = PR_FALSE;
mResetLayer = PR_TRUE;
mActiveTexture = 0;
mSynthesizedGLError = LOCAL_GL_NO_ERROR;
@ -109,7 +110,7 @@ WebGLContext::Invalidate()
if (mInvalidated)
return;
mInvalidated = true;
mInvalidated = PR_TRUE;
HTMLCanvasElement()->InvalidateFrame();
}
@ -195,6 +196,7 @@ WebGLContext::SetDimensions(PRInt32 width, PRInt32 height)
mWidth = width;
mHeight = height;
mResetLayer = PR_TRUE;
// increment the generation number
++mGeneration;
@ -313,14 +315,28 @@ WebGLContext::GetThebesSurface(gfxASurface **surface)
return NS_ERROR_NOT_AVAILABLE;
}
static PRUint8 gWebGLLayerUserData;
already_AddRefed<layers::CanvasLayer>
WebGLContext::GetCanvasLayer(LayerManager *manager)
WebGLContext::GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager)
{
nsRefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
if (!mResetLayer && aOldLayer &&
aOldLayer->GetUserData() == &gWebGLLayerUserData) {
NS_ADDREF(aOldLayer);
if (mInvalidated) {
aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
mInvalidated = PR_FALSE;
}
return aOldLayer;
}
nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
if (!canvasLayer) {
NS_WARNING("CreateCanvasLayer returned null!");
return nsnull;
}
canvasLayer->SetUserData(&gWebGLLayerUserData);
CanvasLayer::Data data;
@ -351,6 +367,7 @@ WebGLContext::GetCanvasLayer(LayerManager *manager)
canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
mInvalidated = PR_FALSE;
mResetLayer = PR_FALSE;
return canvasLayer.forget().get();
}

View File

@ -304,7 +304,8 @@ public:
return ErrorInvalidEnum("%s: invalid enum value", info);
}
already_AddRefed<CanvasLayer> GetCanvasLayer(LayerManager *manager);
already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager);
void MarkContextClean() { }
// a number that increments every time we have an event that causes
@ -321,7 +322,8 @@ protected:
PRInt32 mWidth, mHeight;
CheckedUint32 mGeneration;
PRBool mInvalidated;
PRPackedBool mInvalidated;
PRPackedBool mResetLayer;
WebGLuint mActiveTexture;
WebGLenum mSynthesizedGLError;

View File

@ -415,7 +415,8 @@ public:
nsIInputStream **aStream);
NS_IMETHOD GetThebesSurface(gfxASurface **surface);
NS_IMETHOD SetIsOpaque(PRBool isOpaque);
already_AddRefed<CanvasLayer> GetCanvasLayer(LayerManager *manager);
already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager);
void MarkContextClean();
NS_IMETHOD SetIsIPC(PRBool isIPC);
// this rect is in CSS pixels
@ -505,6 +506,7 @@ protected:
PRInt32 mWidth, mHeight;
PRPackedBool mValid;
PRPackedBool mOpaque;
PRPackedBool mResetLayer;
#ifdef MOZ_IPC
PRPackedBool mIPC;
@ -822,7 +824,7 @@ NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
}
nsCanvasRenderingContext2D::nsCanvasRenderingContext2D()
: mValid(PR_FALSE), mOpaque(PR_FALSE)
: mValid(PR_FALSE), mOpaque(PR_FALSE), mResetLayer(PR_TRUE)
#ifdef MOZ_IPC
, mIPC(PR_FALSE)
#endif
@ -1139,6 +1141,7 @@ nsCanvasRenderingContext2D::InitializeWithSurface(nsIDocShell *docShell, gfxASur
mSurface = surface;
mThebes = surface ? new gfxContext(mSurface) : nsnull;
mResetLayer = PR_TRUE;
/* Create dummy surfaces here */
if (mSurface == nsnull || mSurface->CairoStatus() != 0 ||
@ -4074,17 +4077,29 @@ nsCanvasRenderingContext2D::SetMozImageSmoothingEnabled(PRBool val)
return NS_OK;
}
static PRUint8 g2DContextLayerUserData;
already_AddRefed<CanvasLayer>
nsCanvasRenderingContext2D::GetCanvasLayer(LayerManager *manager)
nsCanvasRenderingContext2D::GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (!mValid)
return nsnull;
nsRefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
if (!mResetLayer && aOldLayer &&
aOldLayer->GetUserData() == &g2DContextLayerUserData) {
NS_ADDREF(aOldLayer);
// XXX Need to just update the changed area here
aOldLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
return aOldLayer;
}
nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
if (!canvasLayer) {
NS_WARNING("CreateCanvasLayer returned null!");
return nsnull;
}
canvasLayer->SetUserData(&g2DContextLayerUserData);
CanvasLayer::Data data;
@ -4095,6 +4110,7 @@ nsCanvasRenderingContext2D::GetCanvasLayer(LayerManager *manager)
canvasLayer->SetIsOpaqueContent(mOpaque);
canvasLayer->Updated(nsIntRect(0, 0, mWidth, mHeight));
mResetLayer = PR_FALSE;
return canvasLayer.forget().get();
}

View File

@ -155,7 +155,8 @@ public:
* Helpers called by various users of Canvas
*/
already_AddRefed<CanvasLayer> GetCanvasLayer(LayerManager *aManager);
already_AddRefed<CanvasLayer> GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager);
// Tell the Context that all the current rendering that it's
// invalidated has been displayed to the screen, so that it should

View File

@ -534,12 +534,13 @@ nsHTMLCanvasElement::GetIsOpaque()
}
already_AddRefed<CanvasLayer>
nsHTMLCanvasElement::GetCanvasLayer(LayerManager *aManager)
nsHTMLCanvasElement::GetCanvasLayer(CanvasLayer *aOldLayer,
LayerManager *aManager)
{
if (!mCurrentContext)
return nsnull;
return mCurrentContext->GetCanvasLayer(aManager);
return mCurrentContext->GetCanvasLayer(aOldLayer, aManager);
}
void

View File

@ -441,10 +441,10 @@ static void BuildLayers(nsDisplayListBuilder* aBuilder,
} // anonymous namespace
already_AddRefed<Layer>
FrameLayerBuilder::MakeContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aContainer,
const nsDisplayList& aChildren)
FrameLayerBuilder::GetContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aContainer,
const nsDisplayList& aChildren)
{
// If there's only one layer, then in principle we can try to flatten
// things by returning that layer here. But that adds complexity to
@ -471,6 +471,22 @@ FrameLayerBuilder::MakeContainerLayerFor(nsDisplayListBuilder* aBuilder,
return layer.forget();
}
Layer*
FrameLayerBuilder::GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aItem)
{
// Layers aren't retained yet
return nsnull;
}
/* static */ void
FrameLayerBuilder::InvalidateThebesLayerContents(nsIFrame* aFrame,
const nsRect& aRect)
{
// do nothing; layers aren't retained yet
}
/* static */ void
FrameLayerBuilder::DrawThebesLayer(ThebesLayer* aLayer,
gfxContext* aContext,

View File

@ -43,6 +43,10 @@
class nsDisplayListBuilder;
class nsDisplayList;
class nsDisplayItem;
class nsIFrame;
class nsRect;
class nsIntRegion;
class gfxContext;
namespace mozilla {
@ -53,14 +57,32 @@ public:
typedef mozilla::layers::LayerManager LayerManager;
/**
* Create a container layer for a display item that contains a child
* Get a container layer for a display item that contains a child
* list, either reusing an existing one or creating a new one.
* aContainer may be null, in which case we construct a root layer.
*/
already_AddRefed<Layer> MakeContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aContainer,
const nsDisplayList& aChildren);
already_AddRefed<Layer> GetContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aContainer,
const nsDisplayList& aChildren);
/**
* Get a retained layer for a leaf display item. Returns null if no
* layer is available, in which case the caller will probably need to
* create one.
*/
Layer* GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aItem);
/**
* Call this during invalidation if aFrame has
* the NS_FRAME_HAS_CONTAINER_LAYER state bit. Only the nearest
* ancestor frame of the damaged frame that has
* NS_FRAME_HAS_CONTAINER_LAYER needs to be invalidated this way.
*/
static void InvalidateThebesLayerContents(nsIFrame* aFrame,
const nsRect& aRect);
/**
* This callback must be provided to EndTransaction. The callback data

View File

@ -453,7 +453,7 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
}
nsRefPtr<Layer> root = aBuilder->LayerBuilder()->
MakeContainerLayerFor(aBuilder, layerManager, nsnull, *this);
GetContainerLayerFor(aBuilder, layerManager, nsnull, *this);
if (!root)
return;
@ -1147,7 +1147,7 @@ already_AddRefed<Layer>
nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager) {
nsRefPtr<Layer> layer = aBuilder->LayerBuilder()->
MakeContainerLayerFor(aBuilder, aManager, this, mList);
GetContainerLayerFor(aBuilder, aManager, this, mList);
if (!layer)
return nsnull;

View File

@ -3726,6 +3726,20 @@ nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX,
*
* See bug #452496 for more details.
*/
if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) &&
!(aFlags & INVALIDATE_NO_THEBES_LAYERS)) {
// XXX for now I'm going to assume this is in the local coordinate space
// This only matters for frames with transforms and retained layers,
// which can't happen right now since transforms trigger fallback
// rendering and the display items that trigger layers are nested inside
// the nsDisplayTransform
// XXX need to set INVALIDATE_NO_THEBES_LAYERS for certain kinds of
// invalidation, e.g. video update, 'opacity' change
FrameLayerBuilder::InvalidateThebesLayerContents(this,
aDamageRect + nsPoint(aX, aY));
// Don't need to invalidate any more Thebes layers
aFlags |= INVALIDATE_NO_THEBES_LAYERS;
}
if ((mState & NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS) &&
GetStyleDisplay()->HasTransform()) {
nsRect newDamageRect;
@ -3843,6 +3857,11 @@ nsIFrame::InvalidateOverflowRect()
void
nsIFrame::InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags)
{
if ((mState & NS_FRAME_HAS_CONTAINER_LAYER) &&
!(aFlags & INVALIDATE_NO_THEBES_LAYERS)) {
FrameLayerBuilder::InvalidateThebesLayerContents(this, aDamageRect);
}
PRUint32 flags =
(aFlags & INVALIDATE_IMMEDIATE) ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC;
nsIView* view = GetView();

View File

@ -91,7 +91,8 @@ public:
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager)
{
return static_cast<nsHTMLCanvasFrame*>(mFrame)->BuildLayer(aBuilder, aManager);
return static_cast<nsHTMLCanvasFrame*>(mFrame)->
BuildLayer(aBuilder, aManager, this);
}
};
@ -230,7 +231,8 @@ nsHTMLCanvasFrame::GetInnerArea() const
already_AddRefed<Layer>
nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager)
LayerManager* aManager,
nsDisplayItem* aItem)
{
nsRect area = GetContentRect() + aBuilder->ToReferenceFrame(GetParent());
nsHTMLCanvasElement* element = static_cast<nsHTMLCanvasElement*>(GetContent());
@ -239,7 +241,9 @@ nsHTMLCanvasFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
if (canvasSize.width <= 0 || canvasSize.height <= 0 || area.IsEmpty())
return nsnull;
nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(aManager);
CanvasLayer* oldLayer = static_cast<CanvasLayer*>
(aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
nsRefPtr<CanvasLayer> layer = element->GetCanvasLayer(oldLayer, aManager);
if (!layer)
return nsnull;

View File

@ -48,6 +48,7 @@
#include "ImageLayers.h"
class nsPresContext;
class nsDisplayItem;
nsIFrame* NS_NewHTMLCanvasFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
@ -66,8 +67,9 @@ public:
const nsDisplayListSet& aLists);
already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager);
LayerManager* aManager,
nsDisplayItem* aItem);
/* get the size of the canvas's image */
nsIntSize GetCanvasSize();

View File

@ -161,14 +161,9 @@ typedef PRUint64 nsFrameState;
// continuation, e.g. a bidi continuation.
#define NS_FRAME_IS_FLUID_CONTINUATION NS_FRAME_STATE_BIT(2)
/*
* This bit is obsolete, replaced by HasOverflowRect().
* The definition is left here as a placeholder for now, to remind us
* that this bit is now free to allocate for other purposes.
* // This bit is set when the frame's overflow rect is
* // different from its border rect (i.e. GetOverflowRect() != GetRect())
* NS_FRAME_OUTSIDE_CHILDREN NS_FRAME_STATE_BIT(3)
*/
// This bit is set whenever the frame has one or more associated
// container layers.
#define NS_FRAME_HAS_CONTAINER_LAYER NS_FRAME_STATE_BIT(3)
// If this bit is set, then a reference to the frame is being held
// elsewhere. The frame may want to send a notification when it is
@ -1842,6 +1837,9 @@ public:
* part of the window to another
* @param aFlags INVALIDATE_REASON_SCROLL_REPAINT: set if the invalidation
* was triggered by scrolling
* @param aFlags INVALIDATE_NO_THEBES_LAYERS: don't invalidate the
* ThebesLayers of any container layer owned by an ancestor. Set this
* only if ThebesLayers definitely don't need to be updated.
*/
enum {
INVALIDATE_IMMEDIATE = 0x01,
@ -1849,7 +1847,8 @@ public:
INVALIDATE_REASON_SCROLL_BLIT = 0x04,
INVALIDATE_REASON_SCROLL_REPAINT = 0x08,
INVALIDATE_REASON_MASK = INVALIDATE_REASON_SCROLL_BLIT |
INVALIDATE_REASON_SCROLL_REPAINT
INVALIDATE_REASON_SCROLL_REPAINT,
INVALIDATE_NO_THEBES_LAYERS = 0x10
};
virtual void InvalidateInternal(const nsRect& aDamageRect,
nscoord aOffsetX, nscoord aOffsetY,

View File

@ -57,6 +57,7 @@
#include "nsBoxFrame.h"
#include "nsImageFrame.h"
#include "nsIImageLoadingContent.h"
#include "nsDisplayList.h"
#ifdef ACCESSIBILITY
#include "nsIServiceManager.h"
@ -175,7 +176,8 @@ CorrectForAspectRatio(const gfxRect& aRect, const nsIntSize& aRatio)
already_AddRefed<Layer>
nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager)
LayerManager* aManager,
nsDisplayItem* aItem)
{
nsRect area = GetContentRect() + aBuilder->ToReferenceFrame(GetParent());
nsHTMLVideoElement* element = static_cast<nsHTMLVideoElement*>(GetContent());
@ -239,9 +241,13 @@ nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder,
presContext->AppUnitsToGfxUnits(area.height));
r = CorrectForAspectRatio(r, videoSize);
nsRefPtr<ImageLayer> layer = aManager->CreateImageLayer();
if (!layer)
return nsnull;
nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*>
(aBuilder->LayerBuilder()->GetLeafLayerFor(aBuilder, aManager, aItem));
if (!layer) {
layer = aManager->CreateImageLayer();
if (!layer)
return nsnull;
}
layer->SetContainer(container);
layer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(this));
@ -367,7 +373,7 @@ public:
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager)
{
return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager);
return static_cast<nsVideoFrame*>(mFrame)->BuildLayer(aBuilder, aManager, this);
}
};

View File

@ -52,6 +52,7 @@
#include "ImageLayers.h"
class nsPresContext;
class nsDisplayItem;
nsIFrame* NS_NewVideoFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
@ -116,7 +117,8 @@ public:
#endif
already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager);
LayerManager* aManager,
nsDisplayItem* aItem);
protected:

View File

@ -2,6 +2,8 @@
== image-rendering-test.html image-rendering-ref.html
== size-change-1.html size-change-1-ref.html
!= text-ltr-left.html text-blank.html
!= text-ltr-right.html text-blank.html
!= text-rtl-left.html text-blank.html

View File

@ -0,0 +1,6 @@
<!DOCTYPE html>
<html>
<body>
<div style="width:200px; height:200px; background:lime"></div>
</body>
</html>

View File

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html class="reftest-wait">
<body>
<canvas width="100" height="100" id="c"></canvas>
<script>
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0,0,100,100);
function finishTest() {
c.width = 200;
c.height = 200;
ctx.fillStyle = "lime";
ctx.fillRect(0,0,200,200);
document.documentElement.removeAttribute("class");
}
window.addEventListener("MozReftestInvalidate", finishTest, false);
</script>
</body>
</html>