HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dependencyForwardingSceneIndex.h
Go to the documentation of this file.
1 //
2 // Copyright 2022 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_IMAGING_HD_DEPENDENCY_FORWARDING_SCENE_INDEX_H
8 #define PXR_IMAGING_HD_DEPENDENCY_FORWARDING_SCENE_INDEX_H
9 
11 
12 #include "pxr/imaging/hd/api.h"
14 
15 #include <tbb/concurrent_unordered_map.h>
16 #include <tbb/concurrent_unordered_set.h>
17 #include <tbb/concurrent_vector.h>
18 
19 #include <atomic>
20 
22 
25 
26 
29 {
30 public:
31 
32  static HdDependencyForwardingSceneIndexRefPtr New(
33  HdSceneIndexBaseRefPtr inputScene) {
34  return TfCreateRefPtr(
35  new HdDependencyForwardingSceneIndex(inputScene));
36  }
37 
38  // satisfying HdSceneIndexBase
39  HD_API
40  HdSceneIndexPrim GetPrim(const SdfPath &primPath) const override;
41 
42  HD_API
43  SdfPathVector GetChildPrimPaths(const SdfPath &primPath) const override;
44 
45  // Provided for the sake of unit testing.
46  // By default, or if SetManualGarbageCollect(false) is called,
47  // we call RemoveDeletedEntries at the end of all of the notice
48  // handlers...
49  HD_API
50  void SetManualGarbageCollect(bool manualGarbageCollect);
51 
52 protected:
53  HD_API
54  HdDependencyForwardingSceneIndex(HdSceneIndexBaseRefPtr inputScene);
55 
56  // satisfying HdSingleInputFilteringSceneIndexBase
57  void _PrimsAdded(
58  const HdSceneIndexBase &sender,
59  const HdSceneIndexObserver::AddedPrimEntries &entries) override;
60 
61  void _PrimsRemoved(
62  const HdSceneIndexBase &sender,
63  const HdSceneIndexObserver::RemovedPrimEntries &entries) override;
64 
65  void _PrimsDirtied(
66  const HdSceneIndexBase &sender,
67  const HdSceneIndexObserver::DirtiedPrimEntries &entries) override;
68 private:
69 
70  // -----------------------------------------------------------------------
71 
72  // XXX: Note for future performance improvements: giving dependency schemas
73  // the ability to represent set->set dependencies, and compacting
74  // these locator entries into set->set, could potentially be a big perf
75  // win, since set/set intersections are vastly more efficient than
76  // pairwise locator intersections.
77  //
78  // Since multiple affected prims could be populating _dependedOn locator
79  // entries from different threads, and we use the individual dependency
80  // name for lifetime management, we'd probably need to populate these
81  // individually and then compact the next time we get a chance from
82  // a single threaded API (e.g. notice handler).
83  //
84  // We can compact as follows:
85  // 1. Construct a dictionary of "affectedDataSourceLocator" ->
86  // vector of "dependedOnDataSourceLocator", and then flatten that into
87  // a locator set.
88  // 2. Construct a dictionary of "dependedOnDataSourceLocatorSet" ->
89  // vector of "affectedDataSourceLocator", and then flatten that into
90  // a locator set.
91  struct _LocatorsEntry
92  {
93  HdDataSourceLocator dependedOnDataSourceLocator;
94  HdDataSourceLocator affectedDataSourceLocator;
95  };
96 
97  // The token used as a key here corresponds to the first member of an
98  // HdDependenciesSchema::EntryPair and provides an identifier for a
99  // dependency declaration. An affected prim may depend on more than one
100  // data source of another prim. That identifier is used here for updating
101  // or removing a dependency.
102  using _LocatorsEntryMap = tbb::concurrent_unordered_map<
103  TfToken,
104  _LocatorsEntry,
106 
107 
108  struct _AffectedPrimDependencyEntry
109  {
110  // Gotta define this stuff because the atomic_bool isn't copyable.
111  // We know we'll only be copying in single-threaded cases, so the naive
112  // implementation is fine...
113  // flaggedForDeletion is an atomic_bool so we can use parallel for in
114  // the notice handlers.
115  _AffectedPrimDependencyEntry()
116  : locatorsEntryMap(), flaggedForDeletion(false) {}
117  _AffectedPrimDependencyEntry(const _AffectedPrimDependencyEntry &rhs)
118  : locatorsEntryMap(rhs.locatorsEntryMap)
119  , flaggedForDeletion(rhs.flaggedForDeletion.load()) {}
120  _AffectedPrimDependencyEntry(_AffectedPrimDependencyEntry &&rhs)
121  : locatorsEntryMap(std::move(rhs.locatorsEntryMap))
122  , flaggedForDeletion(rhs.flaggedForDeletion.load()) {}
123  _AffectedPrimDependencyEntry& operator=(
124  const _AffectedPrimDependencyEntry& rhs) {
125  if (this != &rhs) {
126  locatorsEntryMap = rhs.locatorsEntryMap;
127  flaggedForDeletion = rhs.flaggedForDeletion.load();
128  }
129  return *this;
130  }
131 
132  _LocatorsEntryMap locatorsEntryMap;
133  std::atomic_bool flaggedForDeletion;
134  };
135 
136  // Reverse mapping from a depended on prim to its discovered-thus-far
137  // affected prims and data source locators..
138  using _AffectedPrimsDependencyMap = tbb::concurrent_unordered_map<
139  SdfPath,
140  _AffectedPrimDependencyEntry,
141  SdfPath::Hash>;
142 
143 
144  // Top-level map keyed by paths of depended-on paths
145  using _DependedOnPrimsAffectedPrimsMap = tbb::concurrent_unordered_map<
146  SdfPath,
147  _AffectedPrimsDependencyMap,
148  SdfPath::Hash>;
149 
150 
151  // Lazily-populated mapping of depended on paths to the affected paths
152  // and data source locators used for forwarding of dirtying.
153  // NOTE: This is mutable because it can be updated during calls to
154  // GetPrim -- which is defined as const within HdSceneIndexBase.
155  // This is in service of lazy population goals.
156  //
157  // XXX: This map is "dependedOn" -> "affected" -> vec (loc -> loc)
158  // ... a potential optimization we might want to make is to switch the
159  // organization to: "dependedOn" -> loc -> vec ("affected" -> loc).
160  // This would make dependency management more difficult; since dependencies
161  // may be introduced from different threads, we'd probably need to compact
162  // them into the latter representation. But it would introduce more
163  // early-out opportunities in PrimsDirtied, which would be great especially
164  // for prims that have a high dependency fan-out (e.g. scene globals).
165  mutable _DependedOnPrimsAffectedPrimsMap _dependedOnPrimToDependentsMap;
166 
167 
168  // -----------------------------------------------------------------------
169 
170  using _PathSet = tbb::concurrent_unordered_set<SdfPath, SdfPath::Hash>;
171 
172  struct _AffectedPrimToDependsOnPathsEntry
173  {
174  // Gotta define this stuff because the atomic_bool isn't copyable.
175  // We know we'll only be copying in single-threaded cases, so the naive
176  // implementation is fine...
177  _AffectedPrimToDependsOnPathsEntry()
178  : dependsOnPaths(), flaggedForDeletion(false) {}
179  _AffectedPrimToDependsOnPathsEntry(
180  const _AffectedPrimToDependsOnPathsEntry &rhs)
181  : dependsOnPaths(rhs.dependsOnPaths)
182  , flaggedForDeletion(rhs.flaggedForDeletion.load()) {}
183  _AffectedPrimToDependsOnPathsEntry(
184  _AffectedPrimToDependsOnPathsEntry &&rhs)
185  : dependsOnPaths(std::move(rhs.dependsOnPaths))
186  , flaggedForDeletion(rhs.flaggedForDeletion.load()) {}
187  _AffectedPrimToDependsOnPathsEntry& operator=(
188  const _AffectedPrimToDependsOnPathsEntry& rhs) {
189  if (this != &rhs) {
190  dependsOnPaths = rhs.dependsOnPaths;
191  flaggedForDeletion = rhs.flaggedForDeletion.load();
192  }
193  return *this;
194  }
195 
196  _PathSet dependsOnPaths;
197  std::atomic_bool flaggedForDeletion;
198  };
199 
200 
201  using _AffectedPrimToDependsOnPathsEntryMap = tbb::concurrent_unordered_map<
202  SdfPath,
203  _AffectedPrimToDependsOnPathsEntry,
204  SdfPath::Hash>;
205 
206  // lazily-populated set of depended on paths for affected prims. This
207  // is used to update _dependedOnPrimToDependentsMap when a prim's
208  // __dependencies data source is dirtied (or the prim is removed)
209  // NOTE: This is mutable because it can be updated during calls to
210  // GetPrim -- which is defined as const within HdSceneIndexBase.
211  // This is in service of lazy population goals.
212  mutable _AffectedPrimToDependsOnPathsEntryMap
213  _affectedPrimToDependsOnPathsMap;
214 
215  // -----------------------------------------------------------------------
216 
217  void _ClearDependencies(const SdfPath &primPath);
218  void _UpdateDependencies(const SdfPath &primPath) const;
219  void _UpdateDependencies(
220  const SdfPath &primPath,
221  const HdSceneIndexPrim &prim) const;
222  void _ResetDependencies();
223 
224  // -----------------------------------------------------------------------
225 
226  // Dependencies may reasonably describe cycles given that:
227  // 1) Dependancies can exist at different levels of data source nesting
228  // 2) Dependancy declarations can be present from multiple upstream
229  // scene indices -- each of which draws its value from its input.
230  // In that case, it's not a cycle which affects a computed value but
231  // rather indicates to observers of this scene index that a value
232  // should be repulled.
233  //
234  // When following affected paths to propogate dirtiness, we need to detect
235  // cycles to avoiding hanging. This is done is by sending a "visited" set
236  // containing these node keys:
237  struct _VisitedNode
238  {
239  SdfPath primPath;
240  HdDataSourceLocator locator;
241 
242  inline bool operator==(_VisitedNode const &rhs) const noexcept
243  {
244  return primPath == rhs.primPath && locator == rhs.locator;
245  }
246 
247  template <class HashState>
248  friend void TfHashAppend(HashState &h, _VisitedNode const &myObj) {
249  h.Append(myObj.primPath);
250  h.Append(myObj.locator);
251  }
252 
253  inline size_t Hash() const;
254  struct HashFunctor {
255  size_t operator()(_VisitedNode const &node) const {
256  return node.Hash();
257  }
258  };
259  };
260 
261  using _VisitedNodeSet = tbb::concurrent_unordered_set<
262  _VisitedNode,
263  _VisitedNode::HashFunctor>;
264 
265  using _AdditionalDirtiedVector =
266  tbb::concurrent_vector<HdSceneIndexObserver::DirtiedPrimEntry>;
267 
268  // impl for PrimDirtied which handles propogation of PrimDirtied notices
269  // for affected prims/dataSources.
270  void _PrimDirtied(
271  const SdfPath &primPath,
272  const HdDataSourceLocatorSet &sourceLocator,
273  _VisitedNodeSet *visited,
274  _AdditionalDirtiedVector *moreDirtiedEntries,
275  _PathSet *rebuildDependencies);
276 
277  // -----------------------------------------------------------------------
278 
279  // accumulated depended-on prim paths whose affected prims may have been
280  // removed.
281  mutable _PathSet _potentiallyDeletedDependedOnPaths;
282 
283  // Accumulated affected prim paths who may have been deleted. Normally this
284  // is needed to track affected prims which have an entry in
285  // _dependedOnPrimToDependentsMap but which is empty -- and therefore
286  // won't be handled by their dependencies inclusion in
287  // _potentiallyDeletedDependedOnPaths
288  mutable _PathSet _potentiallyDeletedAffectedPaths;
289 
290  // -----------------------------------------------------------------------
291 
292 public:
293  // XXX does thread-unsafe deletion.
294  // NOTE: optional arguments are in service of unit testing to provide
295  // insight in to what was removed.
296  HD_API
298  SdfPathVector *removedAffectedPrimPaths = nullptr,
299  SdfPathVector *removedDependedOnPrimPaths = nullptr);
300 
301 private:
302  bool _manualGarbageCollect;
303 };
304 
305 
306 inline size_t
308 {
309  return TfHash()(*this);
310 }
311 
313 
314 #endif
TfRefPtr< T > TfCreateRefPtr(T *ptr)
Definition: refPtr.h:1190
void _PrimsDirtied(const HdSceneIndexBase &sender, const HdSceneIndexObserver::DirtiedPrimEntries &entries) override
void TfHashAppend(HashState &h, const T &ptr)
Definition: anyWeakPtr.h:220
HD_API void SetManualGarbageCollect(bool manualGarbageCollect)
STATIC_INLINE size_t Hash(const char *s, size_t len)
Definition: farmhash.h:2099
TfRefBase & operator=(TfRefBase const &)
Definition: refBase.h:76
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
HD_API HdDependencyForwardingSceneIndex(HdSceneIndexBaseRefPtr inputScene)
#define HD_API
Definition: api.h:23
Functor to use for hash maps from tokens to other things.
Definition: token.h:149
void _PrimsAdded(const HdSceneIndexBase &sender, const HdSceneIndexObserver::AddedPrimEntries &entries) override
Definition: hash.h:472
bool operator==(const BaseDimensions< T > &a, const BaseDimensions< Y > &b)
Definition: Dimensions.h:137
Definition: token.h:70
HD_API SdfPathVector GetChildPrimPaths(const SdfPath &primPath) const override
std::vector< class SdfPath > SdfPathVector
Definition: path.h:280
HD_API HdSceneIndexPrim GetPrim(const SdfPath &primPath) const override
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:2002
TF_DECLARE_REF_PTRS(HdDependencyForwardingSceneIndex)
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
HD_API void RemoveDeletedEntries(SdfPathVector *removedAffectedPrimPaths=nullptr, SdfPathVector *removedDependedOnPrimPaths=nullptr)
static HdDependencyForwardingSceneIndexRefPtr New(HdSceneIndexBaseRefPtr inputScene)
void _PrimsRemoved(const HdSceneIndexBase &sender, const HdSceneIndexObserver::RemovedPrimEntries &entries) override