зеркало из https://github.com/mozilla/gecko-dev.git
Bug 564991. Part 9: Retain layer trees. r=tnikkel,sr=mats
This commit is contained in:
Родитель
b6eddb8fcc
Коммит
6824d17ea0
|
@ -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>
|
Загрузка…
Ссылка в новой задаче