summaryrefslogtreecommitdiffstats
path: root/gfx/layers/wr/ClipManager.h
blob: 7253fec8bb59130374d98443cb5a1722613ec74e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */

#ifndef GFX_CLIPMANAGER_H
#define GFX_CLIPMANAGER_H

#include <stack>
#include <unordered_map>

#include "mozilla/Attributes.h"
#include "mozilla/webrender/WebRenderAPI.h"

namespace mozilla {

class nsDisplayItem;
struct ActiveScrolledRoot;
struct DisplayItemClipChain;

namespace wr {
class DisplayListBuilder;
}

namespace layers {

class StackingContextHelper;
class WebRenderLayerManager;

/**
 * This class manages creating and assigning scroll layers and clips in
 * WebRender based on the gecko display list. It has a few public functions that
 * are intended to be invoked while traversing the Gecko display list, and it
 * uses the ASR and clip information from the display list to create the
 * necessary clip state in WebRender.
 *
 * The structure of the clip state in WebRender ends up quite similar to how
 * it is in Gecko. For each ASR in Gecko, we create a scroll layer (i.e. a
 * scrolling clip) in WebRender; these form a tree structure similar to the
 * ASR tree structure. Ancestors of scroll layers are always other scroll
 * layers, or the root scroll node.
 * The DisplayItemClipChain list of clips from the gecko display list is
 * converted to a WR clip chain and pushed on the stack prior to creating
 * any WR commands for that item, and is popped afterwards. In addition,
 * the WR clip chain has a parent pointer, which points to the clip chain for
 * any enclosing stacking context. This again results in a strucuture very
 * similar to that in Gecko, where the clips from container display items get
 * applied to the contained display items.
 */
class ClipManager {
 public:
  ClipManager();

  void BeginBuild(WebRenderLayerManager* aManager,
                  wr::DisplayListBuilder& aBuilder);
  void EndBuild();

  void BeginList(const StackingContextHelper& aStackingContext);
  void EndList(const StackingContextHelper& aStackingContext);

  wr::WrSpaceAndClipChain SwitchItem(nsDisplayListBuilder* aBuilder,
                                     nsDisplayItem* aItem);
  ~ClipManager();

  void PushOverrideForASR(const ActiveScrolledRoot* aASR,
                          const wr::WrSpatialId& aSpatialId);
  void PopOverrideForASR(const ActiveScrolledRoot* aASR);

 private:
  wr::WrSpatialId SpatialIdAfterOverride(const wr::WrSpatialId& aSpatialId);
  wr::WrSpatialId GetScrollLayer(const ActiveScrolledRoot* aASR);

  Maybe<wr::WrSpatialId> DefineScrollLayers(const ActiveScrolledRoot* aASR,
                                            nsDisplayItem* aItem);

  Maybe<wr::WrClipChainId> DefineClipChain(const DisplayItemClipChain* aChain,
                                           int32_t aAppUnitsPerDevPixel);

  WebRenderLayerManager* MOZ_NON_OWNING_REF mManager;
  wr::DisplayListBuilder* mBuilder;

  // Stack of clip caches. Each cache contains a map from gecko
  // DisplayItemClipChain objects to webrender WrClipIds, which allows us to
  // avoid redefining identical clips in WR. However, the gecko
  // DisplayItemClipChain items get deduplicated quite aggressively, without
  // regard to things like the enclosing reference frame or mask. On the WR
  // side, we cannot deduplicate clips that aggressively. So what we do is
  // any time we enter a new reference frame (for example) we create a new clip
  // cache on mCacheStack. This ensures we continue caching stuff within a given
  // reference frame, but disallow caching stuff across reference frames. In
  // general we need to do this anytime PushOverrideForASR is called, as that is
  // called for the same set of conditions for which we cannot deduplicate
  // clips.
  using ClipIdMap = std::unordered_map<const DisplayItemClipChain*,
                                       AutoTArray<wr::WrClipId, 4>>;
  std::stack<ClipIdMap> mCacheStack;

  // A map that holds the cache overrides created by (a) "out of band" clips,
  // i.e. clips that are generated by display items but that ClipManager
  // doesn't know about and (b) stacking contexts that affect clip positioning.
  // These are called "cache overrides" because while we're inside these things,
  // we cannot use the ASR from the gecko display list as-is. Fundamentally this
  // results from a mismatch between the ASR+clip items on the gecko side and
  // the ClipScrollTree on the WR side; the WR side incorporates things like
  // transforms and stacking context origins while the gecko side manages those
  // differently.
  // Any time ClipManager wants to define a new clip as a child of ASR X, it
  // should first check the cache overrides to see if there is a cache override
  // item ((a) or (b) above) that is already a child of X, and then define that
  // clip as a child of Y instead. This map stores X -> Y, which allows
  // ClipManager to do the necessary lookup. Note that there theoretically might
  // be multiple different "Y" clips (in case of nested cache overrides), which
  // is why we need a stack.
  std::unordered_map<wr::WrSpatialId, std::stack<wr::WrSpatialId>> mASROverride;

  // This holds some clip state for a single nsDisplayItem
  struct ItemClips {
    ItemClips(const ActiveScrolledRoot* aASR,
              const DisplayItemClipChain* aChain, int32_t aAppUnitsPerDevPixel,
              bool aSeparateLeaf);

    // These are the "inputs" - they come from the nsDisplayItem
    const ActiveScrolledRoot* mASR;
    const DisplayItemClipChain* mChain;
    int32_t mAppUnitsPerDevPixel;
    bool mSeparateLeaf;

    // These are the "outputs" - they are pushed to WR as needed
    wr::WrSpatialId mScrollId;
    Maybe<wr::WrClipChainId> mClipChainId;

    void UpdateSeparateLeaf(wr::DisplayListBuilder& aBuilder,
                            int32_t aAppUnitsPerDevPixel);
    bool HasSameInputs(const ItemClips& aOther);
    wr::WrSpaceAndClipChain GetSpaceAndClipChain() const;
  };

  // A stack of ItemClips corresponding to the nsDisplayItem ancestry. Each
  // time we recurse into a nsDisplayItem's child list, this stack size
  // increases by one. The topmost item on the stack is for the display item
  // we are currently processing and items deeper on the stack are for that
  // display item's ancestors.
  std::stack<ItemClips> mItemClipStack;
};

}  // namespace layers
}  // namespace mozilla

#endif