1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 //    names, trademarks, service marks, or product names of the Licensor
11 //    and its affiliates, except as required to comply with Section 4(c) of
12 //    the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 //     http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_USD_USD_INSTANCE_CACHE_H
25 #define PXR_USD_USD_INSTANCE_CACHE_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/usd/instanceKey.h"
29 
30 #include "pxr/usd/sdf/path.h"
31 #include "pxr/base/tf/hashmap.h"
32 
33 #include <tbb/mutex.h>
34 #include <map>
35 #include <unordered_map>
36 #include <vector>
37 
38 PXR_NAMESPACE_OPEN_SCOPE
39 
40 /// \class Usd_InstanceChanges
41 ///
42 /// List of changes to prototype prims due to the discovery of new
43 /// or destroyed instanceable prim indexes.
44 ///
45 class Usd_InstanceChanges
46 {
47 public:
AppendChanges(const Usd_InstanceChanges & c)48     void AppendChanges(const Usd_InstanceChanges& c)
49     {
50         newPrototypePrims.insert(
51             newPrototypePrims.end(),
52             c.newPrototypePrims.begin(),
53             c.newPrototypePrims.end());
54 
55         newPrototypePrimIndexes.insert(
56             newPrototypePrimIndexes.end(),
57             c.newPrototypePrimIndexes.begin(),
58             c.newPrototypePrimIndexes.end());
59 
60         changedPrototypePrims.insert(
61             changedPrototypePrims.end(),
62             c.changedPrototypePrims.begin(),
63             c.changedPrototypePrims.end());
64 
65         changedPrototypePrimIndexes.insert(
66             changedPrototypePrimIndexes.end(),
67             c.changedPrototypePrimIndexes.begin(),
68             c.changedPrototypePrimIndexes.end());
69 
70         deadPrototypePrims.insert(
71             deadPrototypePrims.end(),
72             c.deadPrototypePrims.begin(),
73             c.deadPrototypePrims.end());
74     }
75 
76     /// List of new prototype prims and their corresponding source
77     /// prim indexes.
78     std::vector<SdfPath> newPrototypePrims;
79     std::vector<SdfPath> newPrototypePrimIndexes;
80 
81     /// List of prototype prims that have been changed to use a new
82     /// source prim index.
83     std::vector<SdfPath> changedPrototypePrims;
84     std::vector<SdfPath> changedPrototypePrimIndexes;
85 
86     /// List of prototype prims that no longer have any instances.
87     std::vector<SdfPath> deadPrototypePrims;
88 };
89 
90 /// \class Usd_InstanceCache
91 ///
92 /// Private helper object for computing and caching instance information
93 /// on a UsdStage.  This object is responsible for keeping track of the
94 /// instanceable prim indexes and their corresponding prototypes.
95 /// This includes:
96 ///
97 ///    - Tracking all instanceable prim indexes and prototype prims
98 ///      on the stage.
99 ///    - Determining when a new prototype must be created or an
100 ///      old prototype can be reused for a newly-discovered instanceable
101 ///      prim index.
102 ///    - Determining when a prototype can be removed due to it no longer
103 ///      having any instanceable prim indexes.
104 ///
105 /// During composition, UsdStage will discover instanceable prim indexes
106 /// which will be registered with this cache. These prim indexes will
107 /// then be assigned to the appropriate prototype prim. One of these prim
108 /// indexes will be used as the "source" prim index for the prototype.
109 /// This object keeps track of the dependencies formed between
110 /// prototypes and prim indexes by this process.
111 ///
112 /// API note: It can be confusing to reason about prototypes and instances,
113 /// especially with arbitrarily nested instancing.  To help clarify, the API
114 /// below uses two idioms to describe the two main kinds of relationships
115 /// involved in instancing: 1) instances to their prototype usd prims, and 2)
116 /// prototype usd prims to the prim indexes they use.  For #1, we use phrasing
117 /// like, "prototype for instance". For example
118 /// GetPathInPrototypeForInstancePath() finds the corresponding prototype prim
119 /// for a given instance prim path.  For #2, we use phrasing like, "prototype
120 /// using prim index".  For example GetPrototypeUsingPrimIndexPath() finds the
121 /// prototype using the given prim index path as its source, if there is one.
122 ///
123 class Usd_InstanceCache
124 {
125     Usd_InstanceCache(Usd_InstanceCache const &) = delete;
126     Usd_InstanceCache &operator=(Usd_InstanceCache const &) = delete;
127 public:
128     Usd_InstanceCache();
129 
130     /// Registers the given instance prim index \p index with the cache.
131     /// The index will be added to a list of pending changes and will
132     /// not take effect until a subsequent call to ProcessChanges.
133     ///
134     /// It is safe to call this function concurrently from multiple
135     /// threads.
136     ///
137     /// Returns true if the given instance prim index requires a new
138     /// prototype prim or is the source for an existing prototype prim, false
139     /// otherwise.
140     bool RegisterInstancePrimIndex(const PcpPrimIndex& index,
141                                    const UsdStagePopulationMask *mask,
142                                    const UsdStageLoadRules &loadRules);
143 
144     /// Unregisters all instance prim indexes at or under \p primIndexPath.
145     /// The indexes will be added to a list of pending changes and will
146     /// not take effect until a subsequent call to ProcessChanges.
147     void UnregisterInstancePrimIndexesUnder(const SdfPath& primIndexPath);
148 
149     /// Process all instance prim indexes that have been registered or
150     /// unregistered since the last call to this function and return the
151     /// resulting list of prototype prim changes via \p changes.
152     void ProcessChanges(Usd_InstanceChanges* changes);
153 
154     /// Return true if \p path identifies a prototype or a prototype descendant.
155     /// The \p path must be either an absolute path or empty.
156     static bool IsPathInPrototype(const SdfPath& path);
157 
158     /// Return true if \p path identifies a prototype.
159     static bool IsPrototypePath(const SdfPath& path);
160 
161     /// Return instance prim indexes registered for \p prototypePath, an empty
162     /// vector otherwise
163     std::vector<SdfPath> GetInstancePrimIndexesForPrototype(
164             const SdfPath& prototype) const;
165 
166     /// Returns the paths of all prototype prims for instance prim
167     /// indexes registered with this cache.
168     std::vector<SdfPath> GetAllPrototypes() const;
169 
170     /// Returns the number of prototype prims assigned to instance
171     /// prim indexes registered with this cache.
172     size_t GetNumPrototypes() const;
173 
174     /// Return the path of the prototype root prim using the prim index at
175     /// \p primIndexPath as its source prim index, or the empty path if no such
176     /// prototype exists.
177     ///
178     /// Unlike GetPrototypeForPrimIndexPath, this function will return a
179     /// prototype prim path only if the prototype prim is using the specified
180     /// prim index as its source.
181     SdfPath GetPrototypeUsingPrimIndexPath(const SdfPath& primIndexPath) const;
182 
183     /// Return the paths of all prims in prototypes using the prim index at
184     /// \p primIndexPath.
185     ///
186     /// There are at most two such paths.  Without nested instancing, there is
187     /// at most one: the prim in the prototype corresponding to the instance
188     /// identified by \p primIndexPath.  With nested instancing there will be
189     /// two if the \p primIndexPath identifies an instanceable prim index
190     /// descendant to another instancable prim index, and this \p primIndexPath
191     /// was selected for use by that nested instance's prototype.  In that case
192     /// this function will return the path of the nested instance under the
193     /// outer prototype, and also the prototype path corresponding to that
194     /// nested instance.
195     std::vector<SdfPath>
196     GetPrimsInPrototypesUsingPrimIndexPath(const SdfPath& primIndexPath) const;
197 
198     /// Return a vector of pair of prototype and respective source prim index
199     /// path for all prototypes using the prim index at \p primIndexPath or as
200     /// descendent of \p primIndexPath.
201     std::vector<std::pair<SdfPath, SdfPath>>
202     GetPrototypesUsingPrimIndexPathOrDescendents(
203         const SdfPath& primIndexPath) const;
204 
205     /// Return true if a prim in a prototype uses the prim index at
206     /// \p primIndexPath.
207     bool PrototypeUsesPrimIndexPath(const SdfPath& primIndexPath) const;
208 
209     /// Return the path of the prototype prim associated with the instanceable
210     /// \p primIndexPath.  If \p primIndexPath is not instanceable, or if it
211     /// has no associated prototype because it lacks composition arcs, return
212     /// the empty path.
213     SdfPath
214     GetPrototypeForInstanceablePrimIndexPath(
215         const SdfPath& primIndexPath) const;
216 
217     /// Returns true if \p primPath is descendent to an instance.  That is,
218     /// return true if a strict ancestor path of \p usdPrimPath identifies an
219     /// instanceable prim index.
220     bool IsPathDescendantToAnInstance(const SdfPath& primPath) const;
221 
222     /// Returns the shortest ancestor of \p primPath that identifies an
223     /// instanceable prim.  If there is no such ancestor, return the empty path.
224     SdfPath GetMostAncestralInstancePath(const SdfPath &primPath) const;
225 
226     /// Return the corresponding prototype prim path if \p primPath is
227     /// descendant to an instance (see IsPathDescendantToAnInstance()),
228     /// otherwise the empty path.
229     SdfPath GetPathInPrototypeForInstancePath(const SdfPath& primPath) const;
230 
231 private:
232     typedef std::vector<SdfPath> _PrimIndexPaths;
233 
234     void _CreateOrUpdatePrototypeForInstances(
235         const Usd_InstanceKey& instanceKey,
236         _PrimIndexPaths* primIndexPaths,
237         Usd_InstanceChanges* changes,
238         std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> const &
239         prototypeToOldSourceIndexPath);
240 
241     void _RemoveInstances(
242         const Usd_InstanceKey& instanceKey,
243         const _PrimIndexPaths& primIndexPaths,
244         Usd_InstanceChanges* changes,
245         std::unordered_map<SdfPath, SdfPath, SdfPath::Hash> *
246         prototypeToOldSourceIndexPath);
247 
248     void _RemovePrototypeIfNoInstances(
249         const Usd_InstanceKey& instanceKey,
250         Usd_InstanceChanges* changes);
251 
252     bool _PrototypeUsesPrimIndexPath(
253         const SdfPath& primIndexPath,
254         std::vector<SdfPath>* prototypePaths = nullptr) const;
255 
256     SdfPath _GetNextPrototypePath(const Usd_InstanceKey& key);
257 
258 private:
259     tbb::spin_mutex _mutex;
260 
261     // Mapping from instance key <-> prototype prim path.
262     // This stores the path of the prototype prim that should be used
263     // for all instanceable prim indexes with the given instance key.
264     typedef TfHashMap<Usd_InstanceKey, SdfPath, boost::hash<Usd_InstanceKey> >
265         _InstanceKeyToPrototypeMap;
266     typedef TfHashMap<SdfPath, Usd_InstanceKey, SdfPath::Hash>
267         _PrototypeToInstanceKeyMap;
268     _InstanceKeyToPrototypeMap _instanceKeyToPrototypeMap;
269     _PrototypeToInstanceKeyMap _prototypeToInstanceKeyMap;
270 
271     // Mapping from instance prim index path <-> prototype prim path.
272     // This map stores which prim index serves as the source index
273     // for a given prototype prim.
274     typedef std::map<SdfPath, SdfPath> _SourcePrimIndexToPrototypeMap;
275     typedef std::map<SdfPath, SdfPath> _PrototypeToSourcePrimIndexMap;
276     _SourcePrimIndexToPrototypeMap _sourcePrimIndexToPrototypeMap;
277     _PrototypeToSourcePrimIndexMap _prototypeToSourcePrimIndexMap;
278 
279     // Mapping from prototype prim path <-> list of instanceable prim indexes
280     // This map stores which instanceable prim indexes have been assigned to
281     // a prototype prim.
282     typedef std::map<SdfPath, _PrimIndexPaths> _PrototypeToPrimIndexesMap;
283     typedef std::map<SdfPath, SdfPath> _PrimIndexToPrototypeMap;
284     _PrototypeToPrimIndexesMap _prototypeToPrimIndexesMap;
285     _PrimIndexToPrototypeMap _primIndexToPrototypeMap;
286 
287     // Map from instance key -> list of prim index paths
288     // These maps contain lists of pending changes and are the only containers
289     // that should be modified during registration and unregistration.
290     typedef TfHashMap<
291         Usd_InstanceKey, _PrimIndexPaths, boost::hash<Usd_InstanceKey> >
292         _InstanceKeyToPrimIndexesMap;
293     _InstanceKeyToPrimIndexesMap _pendingAddedPrimIndexes;
294     _InstanceKeyToPrimIndexesMap _pendingRemovedPrimIndexes;
295 
296     // Index of last prototype prim created. Used to create
297     // prototype prim names.
298     size_t _lastPrototypeIndex;
299 };
300 
301 PXR_NAMESPACE_CLOSE_SCOPE
302 
303 #endif // PXR_USD_USD_INSTANCE_CACHE_H
304