1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef GFX_CLIPMANAGER_H
8 #define GFX_CLIPMANAGER_H
9 
10 #include <stack>
11 #include <unordered_map>
12 
13 #include "mozilla/Attributes.h"
14 #include "mozilla/webrender/WebRenderAPI.h"
15 
16 class nsDisplayItem;
17 
18 namespace mozilla {
19 
20 struct ActiveScrolledRoot;
21 struct DisplayItemClipChain;
22 
23 namespace wr {
24 class DisplayListBuilder;
25 }
26 
27 namespace layers {
28 
29 class StackingContextHelper;
30 class WebRenderLayerManager;
31 
32 /**
33  * This class manages creating and assigning scroll layers and clips in
34  * WebRender based on the gecko display list. It has a few public functions that
35  * are intended to be invoked while traversing the Gecko display list, and it
36  * uses the ASR and clip information from the display list to create the
37  * necessary clip state in WebRender.
38  *
39  * The structure of the clip state in WebRender ends up quite similar to how
40  * it is in Gecko. For each ASR in Gecko, we create a scroll layer (i.e. a
41  * scrolling clip) in WebRender; these form a tree structure similar to the
42  * ASR tree structure. Ancestors of scroll layers are always other scroll
43  * layers, or the root scroll node.
44  * The DisplayItemClipChain list of clips from the gecko display list is
45  * converted to a WR clip chain and pushed on the stack prior to creating
46  * any WR commands for that item, and is popped afterwards. In addition,
47  * the WR clip chain has a parent pointer, which points to the clip chain for
48  * any enclosing stacking context. This again results in a strucuture very
49  * similar to that in Gecko, where the clips from container display items get
50  * applied to the contained display items.
51  */
52 class ClipManager {
53  public:
54   ClipManager();
55 
56   void BeginBuild(WebRenderLayerManager* aManager,
57                   wr::DisplayListBuilder& aBuilder);
58   void EndBuild();
59 
60   void BeginList(const StackingContextHelper& aStackingContext);
61   void EndList(const StackingContextHelper& aStackingContext);
62 
63   wr::WrSpaceAndClipChain SwitchItem(nsDisplayItem* aItem);
64   ~ClipManager();
65 
66   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
67                           const wr::WrSpatialId& aSpatialId);
68   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
69 
70  private:
71   wr::WrSpatialId SpatialIdAfterOverride(const wr::WrSpatialId& aSpatialId);
72 
73   Maybe<wr::WrSpaceAndClip> GetScrollLayer(const ActiveScrolledRoot* aASR);
74 
75   Maybe<wr::WrSpaceAndClip> DefineScrollLayers(const ActiveScrolledRoot* aASR,
76                                                nsDisplayItem* aItem);
77 
78   Maybe<wr::WrClipChainId> DefineClipChain(const DisplayItemClipChain* aChain,
79                                            int32_t aAppUnitsPerDevPixel);
80 
81   WebRenderLayerManager* MOZ_NON_OWNING_REF mManager;
82   wr::DisplayListBuilder* mBuilder;
83 
84   // Stack of clip caches. Each cache contains a map from gecko
85   // DisplayItemClipChain objects to webrender WrClipIds, which allows us to
86   // avoid redefining identical clips in WR. However, the gecko
87   // DisplayItemClipChain items get deduplicated quite aggressively, without
88   // regard to things like the enclosing reference frame or mask. On the WR
89   // side, we cannot deduplicate clips that aggressively. So what we do is
90   // any time we enter a new reference frame (for example) we create a new clip
91   // cache on mCacheStack. This ensures we continue caching stuff within a given
92   // reference frame, but disallow caching stuff across reference frames. In
93   // general we need to do this anytime PushOverrideForASR is called, as that is
94   // called for the same set of conditions for which we cannot deduplicate
95   // clips.
96   typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId>
97       ClipIdMap;
98   std::stack<ClipIdMap> mCacheStack;
99 
100   // A map that holds the cache overrides created by (a) "out of band" clips,
101   // i.e. clips that are generated by display items but that ClipManager
102   // doesn't know about and (b) stacking contexts that affect clip positioning.
103   // These are called "cache overrides" because while we're inside these things,
104   // we cannot use the ASR from the gecko display list as-is. Fundamentally this
105   // results from a mismatch between the ASR+clip items on the gecko side and
106   // the ClipScrollTree on the WR side; the WR side incorporates things like
107   // transforms and stacking context origins while the gecko side manages those
108   // differently.
109   // Any time ClipManager wants to define a new clip as a child of ASR X, it
110   // should first check the cache overrides to see if there is a cache override
111   // item ((a) or (b) above) that is already a child of X, and then define that
112   // clip as a child of Y instead. This map stores X -> Y, which allows
113   // ClipManager to do the necessary lookup. Note that there theoretically might
114   // be multiple different "Y" clips (in case of nested cache overrides), which
115   // is why we need a stack.
116   std::unordered_map<wr::WrSpatialId, std::stack<wr::WrSpatialId>> mASROverride;
117 
118   // This holds some clip state for a single nsDisplayItem
119   struct ItemClips {
120     ItemClips(const ActiveScrolledRoot* aASR,
121               const DisplayItemClipChain* aChain, bool aSeparateLeaf);
122 
123     // These are the "inputs" - they come from the nsDisplayItem
124     const ActiveScrolledRoot* mASR;
125     const DisplayItemClipChain* mChain;
126     bool mSeparateLeaf;
127 
128     // These are the "outputs" - they are pushed to WR as needed
129     wr::WrSpatialId mScrollId;
130     Maybe<wr::WrClipChainId> mClipChainId;
131 
132     void UpdateSeparateLeaf(wr::DisplayListBuilder& aBuilder,
133                             int32_t aAppUnitsPerDevPixel);
134     bool HasSameInputs(const ItemClips& aOther);
135     void CopyOutputsFrom(const ItemClips& aOther);
136     wr::WrSpaceAndClipChain GetSpaceAndClipChain() const;
137   };
138 
139   // A stack of ItemClips corresponding to the nsDisplayItem ancestry. Each
140   // time we recurse into a nsDisplayItem's child list, this stack size
141   // increases by one. The topmost item on the stack is for the display item
142   // we are currently processing and items deeper on the stack are for that
143   // display item's ancestors.
144   std::stack<ItemClips> mItemClipStack;
145 };
146 
147 }  // namespace layers
148 }  // namespace mozilla
149 
150 #endif
151