Bug 564991. Part 9: Retain layer trees. r=tnikkel,sr=mats

This commit is contained in:
Robert O'Callahan 2010-07-16 09:07:51 +12:00
Родитель b6eddb8fcc
Коммит 6824d17ea0
10 изменённых файлов: 1499 добавлений и 457 удалений

Просмотреть файл

@ -160,16 +160,24 @@ public:
* The callee must draw all of aRegionToDraw. Drawing outside
* aRegionToDraw will be clipped out or ignored.
* The callee must draw all of aRegionToDraw.
* This region is relative to 0,0 in the ThebesLayer.
*
* aRegionToInvalidate contains a region whose contents have been
* changed by the layer manager and which must therefore be invalidated.
* For example, this could be non-empty if the layer internally switched
* from RGBA to RGB or back ... we might want to repaint it to
* For example, this could be non-empty if a retained layer internally
* switches from RGBA to RGB or back ... we might want to repaint it to
* consistently use subpixel-AA or not.
* This region is relative to 0,0 in the ThebesLayer.
* aRegionToInvalidate may contain areas that are outside
* aRegionToDraw; the callee must ensure that these areas are repainted
* in the current layer manager transaction or in a later layer
* manager transaction.
*
* aContext must not be used after the call has returned.
* We guarantee that buffered contents in the visible
* region are valid once drawing is complete.
*
* The origin of aContext is 0,0 in the ThebesLayer.
*/
typedef void (* DrawThebesLayerCallback)(ThebesLayer* aLayer,
gfxContext* aContext,

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -38,38 +38,113 @@
#ifndef FRAMELAYERBUILDER_H_
#define FRAMELAYERBUILDER_H_
#include "Layers.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsTArray.h"
#include "nsRegion.h"
#include "nsIFrame.h"
class nsDisplayListBuilder;
class nsDisplayList;
class nsDisplayItem;
class nsIFrame;
class nsRect;
class nsIntRegion;
class gfxContext;
namespace mozilla {
namespace layers {
class Layer;
class ThebesLayer;
class LayerManager;
}
/**
* The FrameLayerBuilder belongs to an nsDisplayListBuilder and is
* responsible for converting display lists into layer trees.
*
* The most important API in this class is BuildContainerLayerFor. This
* method takes a display list as input and constructs a ContainerLayer
* with child layers that render the contents of the display list. It
* also updates userdata for the retained layer manager, and
* DisplayItemDataProperty data for frames, to record the relationship
* between frames and layers.
*
* That data enables us to retain layer trees. When constructing a
* ContainerLayer, we first check to see if there's an existing
* ContainerLayer for the same frame that can be recycled. If we recycle
* it, we also try to reuse its existing ThebesLayer children to render
* the display items without layers of their own. The idea is that by
* recycling layers deterministically, we can ensure that when nothing
* changes in a display list, we will reuse the existing layers without
* changes.
*
* We expose a GetLeafLayerFor method that can be called by display items
* that make their own layers (e.g. canvas and video); this method
* locates the last layer used to render the display item, if any, and
* return it as a candidate for recycling.
*
* FrameLayerBuilder sets up ThebesLayers so that 0,0 in the Thebes layer
* corresponds to the (pixel-snapped) top-left of the aActiveScrolledRoot.
* It sets up ContainerLayers so that 0,0 in the container layer
* corresponds to the snapped top-left of the display list reference frame.
*/
class FrameLayerBuilder {
public:
typedef mozilla::layers::Layer Layer;
typedef mozilla::layers::ThebesLayer ThebesLayer;
typedef mozilla::layers::LayerManager LayerManager;
typedef layers::Layer Layer;
typedef layers::ThebesLayer ThebesLayer;
typedef layers::LayerManager LayerManager;
FrameLayerBuilder() :
mRetainingManager(nsnull),
mInvalidateAllThebesContent(PR_FALSE),
mInvalidateAllLayers(PR_FALSE)
{
mNewDisplayItemData.Init();
mThebesLayerItems.Init();
}
/**
* 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.
* Call this to register a layer tree which was retained since the last
* paint.
*/
already_AddRefed<Layer> GetContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsDisplayItem* aContainer,
const nsDisplayList& aChildren);
void BeginUpdatingRetainedLayers(LayerManager* aManager);
/**
* 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.
* Call this whenever we end a transaction on aManager. If aManager
* is not the retained layer manager then it must be a temporary layer
* manager that will not be used again.
*/
void DidEndTransaction(LayerManager* aManager);
/**
* Build a container layer for a display item that contains a child
* list, either reusing an existing one or creating a new one. It
* sets the container layer children to layers which together render
* the contents of the display list. It reuses existing layers from
* the retained layer manager if possible.
* aContainer may be null, in which case we construct a root layer.
* This gets called by display list code. It calls BuildLayer on the
* items in the display list, making items with their own layers
* children of the new container, and assigning all other items to
* ThebesLayer children created and managed by the FrameLayerBuilder.
* Returns a layer with clip rect cleared; it is the
* caller's responsibility to add any clip rect and set the visible
* region.
*/
already_AddRefed<Layer> BuildContainerLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
nsIFrame* aContainerFrame,
nsDisplayItem* aContainerItem,
const nsDisplayList& aChildren);
/**
* Get a retained layer for a display item that needs to create its own
* layer for rendering (i.e. under nsDisplayItem::BuildLayer). Returns
* null if no retained layer is available, which usually means that this
* display item didn't have a layer before so the caller will
* need to create one.
* Returns a layer with clip rect cleared; it is the
* caller's responsibility to add any clip rect and set the visible
* region.
*/
Layer* GetLeafLayerFor(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
@ -84,6 +159,18 @@ public:
static void InvalidateThebesLayerContents(nsIFrame* aFrame,
const nsRect& aRect);
/**
* Call this to force *all* retained layer contents to be discarded at
* the next paint.
*/
static void InvalidateAllThebesLayerContents(LayerManager* aManager);
/**
* Call this to force all retained layers to be discarded and recreated at
* the next paint.
*/
static void InvalidateAllLayers(LayerManager* aManager);
/**
* This callback must be provided to EndTransaction. The callback data
* must be the nsDisplayListBuilder containing this FrameLayerBuilder.
@ -93,6 +180,173 @@ public:
const nsIntRegion& aRegionToDraw,
const nsIntRegion& aRegionToInvalidate,
void* aCallbackData);
#ifdef DEBUG
/**
* Dumps aManager's layer tree to stderr.
*/
static void DumpLayerTree(LayerManager* aManager);
/**
* Dumps this FrameLayerBuilder's retained layer manager's retained
* layer tree to stderr.
*/
void DumpRetainedLayerTree();
#endif
/******* PRIVATE METHODS to FrameLayerBuilder.cpp ********/
/* These are only in the public section because they need
* to be called by file-scope helper functions in FrameLayerBuilder.cpp.
*/
/**
* Record aItem as a display item that is rendered by aLayer.
*/
void AddLayerDisplayItem(Layer* aLayer, nsDisplayItem* aItem);
/**
* Record aItem as a display item that is rendered by the ThebesLayer
* aLayer, with aClipRect, where aContainerLayerFrame is the frame
* for the container layer this ThebesItem belongs to.
* aItem must have an underlying frame.
*/
void AddThebesDisplayItem(ThebesLayer* aLayer, nsDisplayItem* aItem,
const nsRect* aClipRect,
nsIFrame* aContainerLayerFrame);
/**
* Given a frame and a display item key that uniquely identifies a
* display item for the frame, find the layer that was last used to
* render that display item. Returns null if there is no such layer.
* This could be a dedicated layer for the display item, or a ThebesLayer
* that renders many display items.
*/
Layer* GetOldLayerFor(nsIFrame* aFrame, PRUint32 aDisplayItemKey);
protected:
/**
* We store an array of these for each frame that is associated with
* one or more retained layers. Each DisplayItemData records the layer
* used to render one of the frame's display items.
*/
class DisplayItemData {
public:
DisplayItemData(Layer* aLayer, PRUint32 aKey)
: mLayer(aLayer), mDisplayItemKey(aKey) {}
nsRefPtr<Layer> mLayer;
PRUint32 mDisplayItemKey;
};
static void InternalDestroyDisplayItemData(nsIFrame* aFrame,
void* aPropertyValue,
PRBool aRemoveFromFramesWithLayers);
static void DestroyDisplayItemData(nsIFrame* aFrame, void* aPropertyValue);
/**
* For DisplayItemDataProperty, the property value *is* an
* nsTArray<DisplayItemData>, not a pointer to an array. This works
* because sizeof(nsTArray<T>) == sizeof(void*).
*/
NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(DisplayItemDataProperty,
DestroyDisplayItemData)
/**
* We accumulate DisplayItemData elements in a hashtable during
* the paint process, and store them in the frame property only when
* paint is complete. This is the hashentry for that hashtable.
*/
class DisplayItemDataEntry : public nsPtrHashKey<nsIFrame> {
public:
DisplayItemDataEntry(const nsIFrame *key) : nsPtrHashKey<nsIFrame>(key) {}
DisplayItemDataEntry(const DisplayItemDataEntry &toCopy) :
nsPtrHashKey<nsIFrame>(toCopy.mKey), mData(toCopy.mData)
{
NS_ERROR("Should never be called, since we ALLOW_MEMMOVE");
}
PRBool HasContainerLayer();
nsTArray<DisplayItemData> mData;
enum { ALLOW_MEMMOVE = PR_TRUE };
};
/**
* We store one of these for each display item associated with a
* ThebesLayer, in a hashtable that maps each ThebesLayer to an array
* of ClippedDisplayItems. (ThebesLayerItemsEntry is the hash entry
* for that hashtable.)
* These are only stored during the paint process, so that the
* DrawThebesLayer callback can figure out which items to draw for the
* ThebesLayer.
* mItem always has an underlying frame.
*/
struct ClippedDisplayItem {
ClippedDisplayItem(nsDisplayItem* aItem, const nsRect* aClipRect)
: mItem(aItem), mHasClipRect(aClipRect != nsnull)
{
if (mHasClipRect) {
mClipRect = *aClipRect;
}
}
nsDisplayItem* mItem;
nsRect mClipRect;
PRPackedBool mHasClipRect;
};
/**
* We accumulate ClippedDisplayItem elements in a hashtable during
* the paint process. This is the hashentry for that hashtable.
*/
class ThebesLayerItemsEntry : public nsPtrHashKey<ThebesLayer> {
public:
ThebesLayerItemsEntry(const ThebesLayer *key) : nsPtrHashKey<ThebesLayer>(key) {}
ThebesLayerItemsEntry(const ThebesLayerItemsEntry &toCopy) :
nsPtrHashKey<ThebesLayer>(toCopy.mKey), mItems(toCopy.mItems)
{
NS_ERROR("Should never be called, since we ALLOW_MEMMOVE");
}
nsTArray<ClippedDisplayItem> mItems;
nsIFrame* mContainerLayerFrame;
enum { ALLOW_MEMMOVE = PR_TRUE };
};
void RemoveThebesItemsForLayerSubtree(Layer* aLayer);
static PLDHashOperator UpdateDisplayItemDataForFrame(nsPtrHashKey<nsIFrame>* aEntry,
void* aUserArg);
static PLDHashOperator StoreNewDisplayItemData(DisplayItemDataEntry* aEntry,
void* aUserArg);
/**
* The layer manager belonging to the widget that is being retained
* across paints.
*/
LayerManager* mRetainingManager;
/**
* A map from frames to a list of (display item key, layer) pairs that
* describes what layers various parts of the frame are assigned to.
*/
nsTHashtable<DisplayItemDataEntry> mNewDisplayItemData;
/**
* A map from ThebesLayers to the list of display items (plus
* clipping data) to be rendered in the layer.
*/
nsTHashtable<ThebesLayerItemsEntry> mThebesLayerItems;
/**
* Indicates that the contents of all ThebesLayers should be rerendered
* during this paint.
*/
PRPackedBool mInvalidateAllThebesContent;
/**
* Indicates that the entire layer tree should be rerendered
* during this paint.
*/
PRPackedBool mInvalidateAllLayers;
};
}

Просмотреть файл

@ -453,7 +453,7 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
}
nsRefPtr<Layer> root = aBuilder->LayerBuilder()->
GetContainerLayerFor(aBuilder, layerManager, nsnull, *this);
BuildContainerLayerFor(aBuilder, layerManager, aForFrame, nsnull, *this);
if (!root)
return;
@ -464,6 +464,7 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
layerManager->SetRoot(root);
layerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer,
aBuilder);
aBuilder->LayerBuilder()->DidEndTransaction(layerManager);
nsCSSRendering::DidPaint();
}
@ -639,6 +640,24 @@ void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
::Sort(this, Count(), aCmp, aClosure);
}
PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion) {
nsRect bounds = GetBounds(aBuilder);
nsRegion itemVisible;
itemVisible.And(*aVisibleRegion, bounds);
mVisibleRect = itemVisible.GetBounds();
if (mVisibleRect.IsEmpty() ||
!ComputeVisibility(aBuilder, aVisibleRegion, nsnull))
return PR_FALSE;
if (IsOpaque(aBuilder)) {
aVisibleRegion->Sub(*aVisibleRegion, bounds);
}
return PR_TRUE;
}
void nsDisplaySolidColor::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx) {
aCtx->SetColor(mColor);
@ -1147,7 +1166,7 @@ already_AddRefed<Layer>
nsDisplayOpacity::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager) {
nsRefPtr<Layer> layer = aBuilder->LayerBuilder()->
GetContainerLayerFor(aBuilder, aManager, this, mList);
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList);
if (!layer)
return nsnull;

Просмотреть файл

@ -644,6 +644,16 @@ public:
nsDisplayItem* GetAbove() { return mAbove; }
/**
* Like ComputeVisibility, but does the work that nsDisplayList
* does per-item:
* -- Intersects GetBounds with aVisibleRegion and puts the result
* in mVisibleRect
* -- Subtracts bounds from aVisibleRegion if the item is opaque
*/
PRBool RecomputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion);
protected:
friend class nsDisplayList;

Просмотреть файл

@ -677,6 +677,20 @@ nsLayoutUtils::GetScrollableFrameFor(nsIFrame *aScrolledFrame)
return sf;
}
nsIFrame*
nsLayoutUtils::GetActiveScrolledRootFor(nsIFrame* aFrame,
nsIFrame* aStopAtAncestor,
nsPoint* aOffset)
{
// For now, just use aStopAtAncestor --- i.e., treat nothing as active.
// We'll make this more precise when we actually start using layers for
// scrolling.
if (aOffset) {
*aOffset = aFrame->GetOffsetTo(aStopAtAncestor);
}
return aStopAtAncestor;
}
// static
nsIScrollableFrame*
nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,

Просмотреть файл

@ -287,6 +287,17 @@ public:
static PRBool IsAncestorFrameCrossDoc(nsIFrame* aAncestorFrame, nsIFrame* aFrame,
nsIFrame* aCommonAncestor = nsnull);
/**
* Finds the nearest ancestor frame that is the root of an "actively
* scrolled" frame subtree, or aStopAtAncestor if there is no
* such ancestor before we reach aStopAtAncestor in the ancestor chain.
* We expect frames with the same "active scrolled root" to be
* scrolled together, so we'll place them in the same ThebesLayer.
*/
static nsIFrame* GetActiveScrolledRootFor(nsIFrame* aFrame,
nsIFrame* aStopAtAncestor,
nsPoint* aOffset);
/**
* GetFrameFor returns the root frame for a view
* @param aView is the view to return the root frame for

Просмотреть файл

@ -1,3 +1,4 @@
HTTP == fixed-1.html fixed-1.html?ref
HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref
HTTP == simple-1.html simple-1.html?ref
== uncovering-1.html uncovering-1-ref.html

Просмотреть файл

@ -0,0 +1,23 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
width: 1500px;
overflow: hidden;
}
div#bottom {
position: fixed;
left: 0;
top: 0;
height: 200px;
width: 200px;
background: green;
}
</style>
</head>
<body>
<div id="bottom"></div>
<div style="width:600px; height:100px; background:pink;"></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<head>
<style>
body {
width: 1500px;
overflow: hidden;
}
div#bottom {
position: fixed;
left: 0;
top: 0;
height: 200px;
width: 200px;
background: green;
}
</style>
</head>
<body>
<div id="bottom"></div>
<div style="width:600px; height:100px; background:pink;"></div>
<script>
document.documentElement.scrollLeft = 200;
function doTest() {
document.documentElement.removeAttribute("class");
document.documentElement.scrollLeft = 0;
}
window.addEventListener("MozReftestInvalidate", doTest, false);
</script>
</body>
</html>