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_IMAGING_USD_IMAGING_RESOLVED_ATTRIBUTE_CACHE_H
25 #define PXR_USD_IMAGING_USD_IMAGING_RESOLVED_ATTRIBUTE_CACHE_H
26 
27 /// \file
28 
29 #include "pxr/pxr.h"
30 #include "pxr/usdImaging/usdImaging/api.h"
31 #include "pxr/usd/usd/prim.h"
32 #include "pxr/usd/usd/primRange.h"
33 #include "pxr/usd/usdShade/tokens.h"
34 #include "pxr/usd/sdf/path.h"
35 
36 #include "pxr/base/work/utils.h"
37 
38 #include <boost/functional/hash.hpp>
39 #include <tbb/concurrent_unordered_map.h>
40 #include <functional>
41 
42 PXR_NAMESPACE_OPEN_SCOPE
43 
44 /// A general caching mechanism for attributes that are nontrivial to resolve,
45 /// such as attributes inherited up or down the ancestor chain or attributes
46 /// with significant load-time processing involved.
47 ///
48 /// This class is thread safe following the basic guarantee that calling const
49 /// methods are thread safe, non-const methods are not.
50 ///
51 /// This cache is generalized based on a strategy object which dictates what
52 /// value type it will hold along with a "query" object, which can be as simple
53 /// as a UsdObject or in the case of Xform cache, we use something more fancy, a
54 /// UsdGeomXformable::XformQuery. This cache is thread safe and lock free. It is
55 /// not wait free, however waits are expected to be extremely short (a small
56 /// number of cycles).
57 ///
58 /// An optional implementation data (ImplData) object may be used for computing
59 /// the values to be cached, if necessary. This object is passed along to the
60 /// MakeQuery() method of the strategy object, making it available for use in
61 /// computations. If MakeQuery() is expected to modify the ImplData object in
62 /// any way, care must be taken to ensure that the modifications are
63 /// thread-safe. The fallback type for ImplData is bool, when it's not specified
64 /// by a cache.
65 ///
66 template<typename Strategy, typename ImplData=bool>
67 class UsdImaging_ResolvedAttributeCache
68 {
69     friend Strategy;
70     struct _Entry;
71     typedef tbb::concurrent_unordered_map<UsdPrim,
72                                           _Entry,
73                                           boost::hash<UsdPrim> > _CacheMap;
74 public:
75     typedef typename Strategy::value_type value_type;
76     typedef typename Strategy::query_type query_type;
77 
78     typedef TfHashMap<UsdPrim, value_type, boost::hash<UsdPrim> >
79         ValueOverridesMap;
80 
81     /// Construct a new for the specified \p time.
82     explicit UsdImaging_ResolvedAttributeCache(
83         const UsdTimeCode time,
84         ImplData *implData=nullptr,
85         const ValueOverridesMap valueOverrides=ValueOverridesMap())
_time(time)86         : _time(time)
87         , _rootPath(SdfPath::AbsoluteRootPath())
88         , _cacheVersion(_GetInitialCacheVersion())
89         , _valueOverrides(valueOverrides)
90         , _implData(implData)
91     {
92     }
93 
94     /// Construct a new cache for UsdTimeCode::Default().
UsdImaging_ResolvedAttributeCache()95     UsdImaging_ResolvedAttributeCache()
96         : _time(UsdTimeCode::Default())
97         , _rootPath(SdfPath::AbsoluteRootPath())
98         , _cacheVersion(1)
99     {
100     }
101 
~UsdImaging_ResolvedAttributeCache()102     ~UsdImaging_ResolvedAttributeCache()
103     {
104         WorkSwapDestroyAsync(_cache);
105     }
106 
107 
108     /// Compute the inherited value for the given \p prim, including the value
109     /// authored on the Prim itself, if present.
GetValue(const UsdPrim & prim)110     value_type GetValue(const UsdPrim& prim) const
111     {
112         TRACE_FUNCTION();
113         if (!prim.GetPath().HasPrefix(_rootPath)
114             && !prim.IsInPrototype()) {
115             TF_CODING_ERROR("Attempt to get value for: %s "
116                             "which is not within the specified root: %s",
117                             prim.GetPath().GetString().c_str(),
118                             _rootPath.GetString().c_str());
119             return Strategy::MakeDefault();
120         }
121 
122         return *_GetValue(prim);
123     }
124 
125     /// Returns the underlying query object for the given prim. If the prim has
126     /// no cache entry, calling this method will trigger the entry to be
127     /// populated in an invalid state, but will return a valid query object.
128     query_type const*
GetQuery(const UsdPrim & prim)129     GetQuery(const UsdPrim& prim) const {
130         return &_GetCacheEntryForPrim(prim)->query;
131     }
132 
133     /// Clears all pre-cached values.
Clear()134     void Clear() {
135         WorkSwapDestroyAsync(_cache);
136         _cacheVersion = _GetInitialCacheVersion();
137     }
138 
139     /// Use the new \p time when computing values and may clear any existing
140     /// values cached for the previous time. Setting \p time to the current time
141     /// is a no-op.
SetTime(UsdTimeCode time)142     void SetTime(UsdTimeCode time) {
143         if (time == _time)
144             return;
145 
146         if (Strategy::ValueMightBeTimeVarying()) {
147             // Mark all cached entries as invalid, but leave the queries behind.
148             // We increment by 2 here and always keep the version an odd number,
149             // this enables the use of even versions as a per-entry spin lock.
150             _cacheVersion += 2;
151         }
152 
153         // Update to correct time.
154         _time = time;
155     }
156 
157     /// Get the current time from which this cache is reading values.
GetTime()158     UsdTimeCode GetTime() const { return _time; }
159 
160     /// Set the root ancestor path at which to stop inheritance.
161     /// Note that values on the root are not inherited.
162     ///
163     /// In general, you shouldn't use this function; USD inherited attribute
164     /// resolution will traverse to the pseudo-root, and not doing that in the
165     /// cache can introduce subtle bugs. This exists mainly for the benefit of
166     /// the transform cache, since UsdImagingDelegate transform resolution
167     /// semantics are complicated and special-cased.
SetRootPath(const SdfPath & rootPath)168     void SetRootPath(const SdfPath& rootPath) {
169         if (!rootPath.IsAbsolutePath()) {
170             TF_CODING_ERROR("Invalid root path: %s",
171                             rootPath.GetString().c_str());
172             return;
173         }
174 
175         if (rootPath == _rootPath)
176             return;
177 
178         Clear();
179         _rootPath = rootPath;
180     }
181 
182     /// Return the root ancestor path at which to stop inheritance.
183     /// See notes on SetRootPath.
GetRootPath()184     const SdfPath & GetRootPath() const { return _rootPath; }
185 
186     /// Helper function used to append, update or remove overrides from the
187     /// internal value overrides map. By doing the updates to the map in a
188     /// single pass, we can optimize the dirtying of the cache entries.
189     ///
190     /// \p valueOverrides contains the set of value overrides to be appended
191     /// or updated in the internal value overrides map.
192     /// \p overriesToRemove contains the list of prims for which overrides
193     /// must be removed.
194     /// \p dirtySubtreeRoots is populated with the list of paths to the roots
195     /// of the subtrees that must be recomputed.
UpdateValueOverrides(const ValueOverridesMap & valueOverrides,const std::vector<UsdPrim> & overridesToRemove,std::vector<SdfPath> * dirtySubtreeRoots)196     void UpdateValueOverrides(const ValueOverridesMap &valueOverrides,
197                               const std::vector<UsdPrim> &overridesToRemove,
198                               std::vector<SdfPath> *dirtySubtreeRoots)
199     {
200         TRACE_FUNCTION();
201 
202         if (valueOverrides.empty() && overridesToRemove.empty())
203             return;
204 
205         ValueOverridesMap valueOverridesToProcess;
206         SdfPathVector processedOverridePaths;
207         TF_FOR_ALL(it, valueOverrides) {
208             const UsdPrim &prim = it->first;
209             const value_type &value = it->second;
210 
211             // If the existing value matches the incoming value, skip
212             // the update and dirtying.
213             if (*_GetValue(prim) == value)
214                 continue;
215 
216             valueOverridesToProcess[prim] = value;
217         }
218 
219         TF_FOR_ALL(it, valueOverridesToProcess) {
220             const UsdPrim &prim = it->first;
221             const value_type &value = it->second;
222 
223             // XXX: performance
224             // We could probably make this faster by using a hash table of
225             // prefixes. This hasn't showed up in traces much though as it's not
226             // common to update value overrides for more than one path at a
227             // time.
228             bool isDescendantOfProcessedOverride = false;
229             for (const SdfPath &processedPath : processedOverridePaths) {
230                 if (prim.GetPath().HasPrefix(processedPath)) {
231                     isDescendantOfProcessedOverride = true;
232                     break;
233                 }
234             }
235 
236             // Invalidate cache entries if the prim is not a descendant of a
237             // path that has already been processed.
238             if (!isDescendantOfProcessedOverride) {
239                 for (UsdPrim descendant: UsdPrimRange(prim)) {
240                     if (_Entry* entry = _GetCacheEntryForPrim(descendant)) {
241                         entry->version = _GetInvalidVersion();
242                     }
243                 }
244                 processedOverridePaths.push_back(prim.GetPath());
245                 dirtySubtreeRoots->push_back(prim.GetPath());
246             }
247 
248             // Update overrides in the internal value overrides map.
249             _valueOverrides[prim] = value;
250         }
251 
252         for (const UsdPrim &prim : overridesToRemove) {
253 
254             // Erase the entry from the map of overrides.
255             size_t numErased = _valueOverrides.erase(prim);
256 
257             // If the override doesn't exist, then there's nothing to do.
258             if (numErased == 0) {
259                 continue;
260             }
261 
262             bool isDescendantOfProcessedOverride = false;
263             for (const SdfPath &processedPath : processedOverridePaths) {
264                 if (prim.GetPath().HasPrefix(processedPath)) {
265                     isDescendantOfProcessedOverride = true;
266                     break;
267                 }
268             }
269 
270             // Invalidate cache entries if the prim is not a descendant of a
271             // path that has already been processed.
272             if (!isDescendantOfProcessedOverride) {
273                 for (UsdPrim descendant: UsdPrimRange(prim)) {
274                     if (_Entry* entry = _GetCacheEntryForPrim(descendant)) {
275                         entry->version = _GetInvalidVersion();
276                     }
277                 }
278                 dirtySubtreeRoots->push_back(prim.GetPath());
279                 processedOverridePaths.push_back(prim.GetPath());
280             }
281         }
282     }
283 
284 private:
285     // Cached value entries. Note that because query objects may be caching
286     // non-time varying data, entries may exist in the cache with invalid
287     // values. The version is used to determine validity.
288     struct _Entry {
_Entry_Entry289         _Entry()
290             : value(Strategy::MakeDefault())
291             , version(_GetInitialEntryVersion())
292         { }
293 
_Entry_Entry294         _Entry(const query_type & query_,
295                const value_type& value_,
296                unsigned version_)
297             : query(query_)
298             , value(value_)
299             , version(version_)
300         { }
301 
302         query_type query;
303         value_type value;
304         tbb::atomic<unsigned> version;
305     };
306 
307     // Returns the version number for a valid cache entry
_GetValidVersion()308     unsigned _GetValidVersion() const { return _cacheVersion + 1; }
309 
310     // Returns the version number for an invalid cache entry
_GetInvalidVersion()311     unsigned _GetInvalidVersion() const { return _cacheVersion - 1; }
312 
313     // Initial version numbers
_GetInitialCacheVersion()314     static unsigned _GetInitialCacheVersion() { return 1; }
_GetInitialEntryVersion()315     static unsigned _GetInitialEntryVersion() {
316         return _GetInitialCacheVersion()-1;
317     }
318 
319     // Traverse the hierarchy (order is strategy dependent) and compute the
320     // inherited value.
321     value_type const* _GetValue(const UsdPrim& prim) const;
322 
323     // Helper function to get or create a new entry for a prim in the cache.
324     _Entry* _GetCacheEntryForPrim(const UsdPrim &prim) const;
325 
326     // Sets the value of the given cache entry. If multiple threads attempt to
327     // set the same entry, the first in wins and other threads spin until the
328     // new value is set.
329     void _SetCacheEntryForPrim(const UsdPrim &prim,
330                                value_type const& value,
331                                _Entry* entry) const;
332 
333     // Mutable is required here to allow const methods to update the cache when
334     // it is thread safe, however not all mutations of this map are thread safe.
335     // See underlying map documentation for details.
336     mutable _CacheMap _cache;
337 
338     // The time at which this stack is querying and caching attribute values.
339     UsdTimeCode _time;
340     SdfPath _rootPath;
341 
342     // A serial number indicating the valid state of entries in the cache. When
343     // an entry has an equal or greater value, the entry is valid.
344     tbb::atomic<unsigned> _cacheVersion;
345 
346     // Value overrides for a set of descendents.
347     ValueOverridesMap _valueOverrides;
348 
349     // Supplemental cache if used by this inherited cache.
350     ImplData *_implData;
351 };
352 
353 template<typename Strategy, typename ImplData>
354 void
_SetCacheEntryForPrim(const UsdPrim & prim,value_type const & value,_Entry * entry)355 UsdImaging_ResolvedAttributeCache<Strategy,ImplData>::_SetCacheEntryForPrim(
356     const UsdPrim &prim,
357     value_type const& value,
358     _Entry* entry) const
359 {
360     // Note: _cacheVersion is not allowed to change during cache access.
361     unsigned v = entry->version;
362     if (v < _cacheVersion
363         && entry->version.compare_and_swap(_cacheVersion, v) == v)
364     {
365         entry->value = value;
366         entry->version = _GetValidVersion();
367     } else {
368         while (entry->version != _GetValidVersion()) {
369             // Future work: A suggestion is that rather than literally spinning
370             // here, we should use the pause instruction, which sleeps for one
371             // cycle while allowing hyper threads to continue. Folly has a nice
372             // implementation of this packaged up as "sleeper", which we could
373             // also implement in Work and Arch.
374         }
375     }
376 }
377 
378 template<typename Strategy, typename ImplData>
379 typename UsdImaging_ResolvedAttributeCache<Strategy, ImplData>::_Entry*
_GetCacheEntryForPrim(const UsdPrim & prim)380 UsdImaging_ResolvedAttributeCache<Strategy, ImplData>::_GetCacheEntryForPrim(
381     const UsdPrim &prim) const
382 {
383     typename _CacheMap::const_iterator it = _cache.find(prim);
384     if (it != _cache.end()) {
385         return &it->second;
386     }
387 
388     _Entry e;
389     e.query = Strategy::MakeQuery(prim, _implData);
390     e.value = Strategy::MakeDefault();
391     e.version = _GetInvalidVersion();
392     return &(_cache.insert(
393                         typename _CacheMap::value_type(prim, e)).first->second);
394 }
395 
396 template<typename Strategy, typename ImplData>
397 typename UsdImaging_ResolvedAttributeCache<Strategy, ImplData>::value_type const*
_GetValue(const UsdPrim & prim)398 UsdImaging_ResolvedAttributeCache<Strategy, ImplData>::_GetValue(
399     const UsdPrim& prim) const
400 {
401     static value_type const default_ = Strategy::MakeDefault();
402 
403     // Base case.
404     if (!prim || prim.IsPrototype() || prim.GetPath() == _rootPath)
405         return &default_;
406 
407     _Entry* entry = _GetCacheEntryForPrim(prim);
408     if (entry->version == _GetValidVersion()) {
409         // Cache hit
410         return &entry->value;
411     }
412 
413     // Future work: Suggestion is that when multiple threads are computing the
414     // same value, we could block all but one thread here, possibly rescheduling
415     // blocked threads as continuations, rather than allowing all threads to
416     // continue to race until a cache hit is encountered.
417 
418     // Future work: A suggestion is that we make this iterative instead of
419     // recursive.
420     typename ValueOverridesMap::const_iterator it =
421         _valueOverrides.find(prim);
422     if (it != _valueOverrides.end()) {
423         _SetCacheEntryForPrim(prim, it->second, entry);
424     } else {
425         _SetCacheEntryForPrim(prim,
426                               Strategy::Compute(this, prim, &entry->query),
427                               entry);
428     }
429     return &entry->value;
430 }
431 
432 PXR_NAMESPACE_CLOSE_SCOPE
433 
434 // -------------------------------------------------------------------------- //
435 // Xform Cache
436 // -------------------------------------------------------------------------- //
437 
438 #include "pxr/usd/usdGeom/xformable.h"
439 #include "pxr/base/gf/matrix4d.h"
440 
441 PXR_NAMESPACE_OPEN_SCOPE
442 
443 struct UsdImaging_XfStrategy;
444 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_XfStrategy> UsdImaging_XformCache;
445 
446 struct UsdImaging_XfStrategy {
447     typedef GfMatrix4d value_type;
448     typedef UsdGeomXformable::XformQuery query_type;
449 
450     static
ValueMightBeTimeVaryingUsdImaging_XfStrategy451     bool ValueMightBeTimeVarying() { return true; }
452     static
MakeDefaultUsdImaging_XfStrategy453     value_type MakeDefault() { return GfMatrix4d(1); }
454 
455     static
MakeQueryUsdImaging_XfStrategy456     query_type MakeQuery(UsdPrim const& prim, bool *) {
457         if (UsdGeomXformable xf = UsdGeomXformable(prim))
458             return query_type(xf);
459         return query_type();
460     }
461 
462     static
463     value_type
ComputeUsdImaging_XfStrategy464     Compute(UsdImaging_XformCache const* owner,
465             UsdPrim const& prim,
466             query_type const* query)
467     {
468         value_type xform = MakeDefault();
469         // No need to check query validity here because XformQuery doesn't
470         // support it.
471         query->GetLocalTransformation(&xform, owner->GetTime());
472 
473         return !query->GetResetXformStack()
474                 ? (xform * (*owner->_GetValue(prim.GetParent())))
475                 : xform;
476     }
477 
478     // Compute the full transform, this is not part of the interface required by
479     // the cache.
480     static
481     value_type
ComputeTransformUsdImaging_XfStrategy482     ComputeTransform(UsdPrim const& prim,
483             SdfPath const& rootPath,
484             UsdTimeCode time,
485             const TfHashMap<SdfPath, GfMatrix4d, SdfPath::Hash> &ctmOverrides)
486     {
487         bool reset = false;
488         GfMatrix4d ctm(1.0);
489         GfMatrix4d localXf(1.0);
490         UsdPrim p = prim;
491         while (p && p.GetPath() != rootPath) {
492             const auto &overIt = ctmOverrides.find(p.GetPath());
493             // If there's a ctm override, use it and break out of the loop.
494             if (overIt != ctmOverrides.end()) {
495                 ctm *= overIt->second;
496                 break;
497             } else if (UsdGeomXformable xf = UsdGeomXformable(p)) {
498                 if (xf.GetLocalTransformation(&localXf, &reset, time))
499                     ctm *= localXf;
500                 if (reset)
501                     break;
502             }
503             p = p.GetParent();
504         }
505         return ctm;
506     }
507 };
508 
509 PXR_NAMESPACE_CLOSE_SCOPE
510 
511 // -------------------------------------------------------------------------- //
512 // Visibility Cache
513 // -------------------------------------------------------------------------- //
514 
515 #include "pxr/usd/usdGeom/imageable.h"
516 #include "pxr/base/tf/token.h"
517 #include "pxr/usdImaging/usdImaging/debugCodes.h"
518 
519 PXR_NAMESPACE_OPEN_SCOPE
520 
521 struct UsdImaging_VisStrategy;
522 using UsdImaging_VisCache =
523     UsdImaging_ResolvedAttributeCache<UsdImaging_VisStrategy>;
524 
525 /// Strategy used to cache inherited 'visibility' values, implementing pruning
526 /// visibility semantics.
527 ///
528 struct UsdImaging_VisStrategy {
529     typedef TfToken value_type; // invisible, inherited
530     typedef UsdAttributeQuery query_type;
531 
532     static
ValueMightBeTimeVaryingUsdImaging_VisStrategy533     bool ValueMightBeTimeVarying() { return true; }
534 
535     static
MakeDefaultUsdImaging_VisStrategy536     value_type MakeDefault() { return UsdGeomTokens->inherited; }
537 
538     static
MakeQueryUsdImaging_VisStrategy539     query_type MakeQuery(UsdPrim const& prim, bool *)
540     {
541         if (const UsdGeomImageable xf = UsdGeomImageable(prim)) {
542             return query_type(xf.GetVisibilityAttr());
543         }
544         return query_type();
545     }
546 
547     static
548     value_type
ComputeUsdImaging_VisStrategy549     Compute(UsdImaging_VisCache const* owner,
550             UsdPrim const& prim,
551             query_type const* query)
552     {
553         value_type v = *owner->_GetValue(prim.GetParent());
554 
555         // If prim inherits 'invisible', then it's invisible, due to pruning
556         // visibility.
557         if (v == UsdGeomTokens->invisible) {
558             return v;
559         }
560 
561         // Otherwise, prim's value, if it has one, determines its visibility.
562         if (*query) {
563             query->Get(&v, owner->GetTime());
564         }
565         return v;
566     }
567 
568     static
569     value_type
ComputeVisibilityUsdImaging_VisStrategy570     ComputeVisibility(UsdPrim const& prim, UsdTimeCode time)
571     {
572         return UsdGeomImageable(prim).ComputeVisibility(time);
573     }
574 };
575 
576 // -------------------------------------------------------------------------- //
577 // Purpose Cache
578 // -------------------------------------------------------------------------- //
579 
580 struct UsdImaging_PurposeStrategy;
581 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_PurposeStrategy>
582     UsdImaging_PurposeCache;
583 
584 struct UsdImaging_PurposeStrategy {
585     // For proper inheritance, we need to return the PurposeInfo struct which
586     // stores whether child prims can inherit the parent's computed purpose
587     // when they don't have an authored purpose of their own.
588     typedef UsdGeomImageable::PurposeInfo value_type; // purpose, inherited
589     typedef UsdAttributeQuery query_type;
590 
591     static
MakeDefaultUsdImaging_PurposeStrategy592     value_type MakeDefault() {
593         // Return the fallback default instead of an empty purpose info.
594         return value_type(UsdGeomTokens->default_, false);
595     }
596 
597     static
MakeQueryUsdImaging_PurposeStrategy598     query_type MakeQuery(UsdPrim const& prim, bool *) {
599         UsdGeomImageable im = UsdGeomImageable(prim);
600         return im ? query_type(im.GetPurposeAttr()) : query_type();
601     }
602 
603     static
604     value_type
ComputeUsdImaging_PurposeStrategy605     Compute(UsdImaging_PurposeCache const* owner,
606             UsdPrim const& prim,
607             query_type const* query)
608     {
609         // Fallback to parent if the prim isn't imageable or doesn't have a
610         // purpose attribute. Note that this returns the default purpose if
611         // there's no parent prim.
612         if (!*query) {
613             return *(owner->_GetValue(prim.GetParent()));
614         }
615 
616         // If the prim has an authored purpose value, we get and use that.
617         if (query->HasAuthoredValue()) {
618             value_type info;
619             query->Get(&info.purpose);
620             info.isInheritable = true;
621             return info;
622         }
623 
624         // Otherwise we inherit parent's purpose value, but only if the parent's
625         // purpose is inheritable. An inherited purpose is itself inheritable
626         // by child prims..
627         const value_type *v = owner->_GetValue(prim.GetParent());
628         if (v->isInheritable) {
629             return *v;
630         }
631 
632         // Otherwise, get the fallback value. The fallback purpose will not
633         // be inherited by descendants.
634         value_type info;
635         query->Get(&info.purpose);
636         return info;
637     }
638 
639     static
640     value_type
ComputePurposeInfoUsdImaging_PurposeStrategy641     ComputePurposeInfo(UsdPrim const& prim)
642     {
643         return UsdGeomImageable(prim).ComputePurposeInfo();
644     }
645 };
646 
647 PXR_NAMESPACE_CLOSE_SCOPE
648 
649 // -------------------------------------------------------------------------- //
650 // Hydra MaterialBinding Cache
651 // -------------------------------------------------------------------------- //
652 
653 #include "pxr/usd/usdShade/material.h"
654 #include "pxr/usd/usdShade/materialBindingAPI.h"
655 
656 PXR_NAMESPACE_OPEN_SCOPE
657 
658 struct UsdImaging_MaterialBindingImplData {
659     /// Constructor takes the purpose for which material bindings are to be
660     /// evaluated.
UsdImaging_MaterialBindingImplDataUsdImaging_MaterialBindingImplData661     UsdImaging_MaterialBindingImplData(const TfToken &materialPurpose):
662         _materialPurpose(materialPurpose)
663     {   }
664 
665     /// Destructor invokes ClearCaches(), which does the cache deletion in
666     /// parallel.
~UsdImaging_MaterialBindingImplDataUsdImaging_MaterialBindingImplData667     ~UsdImaging_MaterialBindingImplData() {
668         ClearCaches();
669     }
670 
671     /// Returns the material purpose for which bindings must be computed.
GetMaterialPurposeUsdImaging_MaterialBindingImplData672     const TfToken &GetMaterialPurpose() const {
673         return _materialPurpose;
674     }
675 
676     /// Returns the BindingsCache object to be used when computing resolved
677     /// material bindings.
GetBindingsCacheUsdImaging_MaterialBindingImplData678     UsdShadeMaterialBindingAPI::BindingsCache & GetBindingsCache()
679     { return _bindingsCache; }
680 
681     /// Returns the BindingsCache object to be used when computing resolved
682     /// material bindings.
GetCollectionQueryCacheUsdImaging_MaterialBindingImplData683     UsdShadeMaterialBindingAPI::CollectionQueryCache & GetCollectionQueryCache()
684     { return _collQueryCache; }
685 
686     /// Clears all of the held caches.
687     void ClearCaches();
688 
689 private:
690     const TfToken _materialPurpose;
691     UsdShadeMaterialBindingAPI::BindingsCache _bindingsCache;
692     UsdShadeMaterialBindingAPI::CollectionQueryCache _collQueryCache;
693 };
694 
695 struct UsdImaging_MaterialStrategy;
696 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_MaterialStrategy,
697     UsdImaging_MaterialBindingImplData>
698         UsdImaging_MaterialBindingCache;
699 
700 struct UsdImaging_MaterialStrategy {
701     typedef SdfPath value_type;         // inherited path to bound shader
702     typedef UsdShadeMaterial query_type;
703 
704     using ImplData = UsdImaging_MaterialBindingImplData;
705 
706     static
ValueMightBeTimeVaryingUsdImaging_MaterialStrategy707     bool ValueMightBeTimeVarying() { return false; }
708     static
MakeDefaultUsdImaging_MaterialStrategy709     value_type MakeDefault() { return SdfPath(); }
710 
711     static
MakeQueryUsdImaging_MaterialStrategy712     query_type MakeQuery(
713         UsdPrim const& prim,
714         ImplData *implData)
715     {
716         return UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial(
717                 &implData->GetBindingsCache(),
718                 &implData->GetCollectionQueryCache(),
719                 implData->GetMaterialPurpose());
720     }
721 
722     static
723     value_type
ComputeUsdImaging_MaterialStrategy724     Compute(UsdImaging_MaterialBindingCache const* owner,
725             UsdPrim const& prim,
726             query_type const* query)
727     {
728         TF_DEBUG(USDIMAGING_SHADERS).Msg("Looking for \"preview\" material "
729                 "binding for %s\n", prim.GetPath().GetText());
730         if (*query) {
731             SdfPath binding = query->GetPath();
732             if (!binding.IsEmpty()) {
733                 return binding;
734             }
735         }
736         // query already contains the resolved material binding for the prim.
737         // Hence, we don't need to inherit the binding from the parent here.
738         // Futhermore, it may be wrong to inherit the binding from the parent,
739         // because in the new scheme, a child of a bound prim can be unbound.
740         return value_type();
741     }
742 
743     static
744     value_type
ComputeMaterialPathUsdImaging_MaterialStrategy745     ComputeMaterialPath(UsdPrim const& prim, ImplData *implData) {
746         // We don't need to walk up the namespace here since
747         // ComputeBoundMaterial does it for us.
748         if (UsdShadeMaterial mat = UsdShadeMaterialBindingAPI(prim).
749                     ComputeBoundMaterial(&implData->GetBindingsCache(),
750                                          &implData->GetCollectionQueryCache(),
751                                          implData->GetMaterialPurpose())) {
752             return mat.GetPath();
753         }
754         return value_type();
755     }
756 };
757 
758 PXR_NAMESPACE_CLOSE_SCOPE
759 
760 // -------------------------------------------------------------------------- //
761 // ModelDrawMode Cache
762 // -------------------------------------------------------------------------- //
763 
764 #include "pxr/usd/usdGeom/modelAPI.h"
765 
766 PXR_NAMESPACE_OPEN_SCOPE
767 
768 struct UsdImaging_DrawModeStrategy;
769 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_DrawModeStrategy>
770     UsdImaging_DrawModeCache;
771 
772 struct UsdImaging_DrawModeStrategy
773 {
774     typedef TfToken value_type; // origin, bounds, cards, default, inherited
775     typedef UsdAttributeQuery query_type;
776 
777     static
ValueMightBeTimeVaryingUsdImaging_DrawModeStrategy778     bool ValueMightBeTimeVarying() { return false; }
779     static
MakeDefaultUsdImaging_DrawModeStrategy780     value_type MakeDefault() { return UsdGeomTokens->default_; }
781 
782     static
MakeQueryUsdImaging_DrawModeStrategy783     query_type MakeQuery(UsdPrim const& prim, bool *) {
784         if (UsdAttribute a = UsdGeomModelAPI(prim).GetModelDrawModeAttr())
785             return query_type(a);
786         return query_type();
787     }
788 
789     static
790     value_type
ComputeUsdImaging_DrawModeStrategy791     Compute(UsdImaging_DrawModeCache const* owner,
792             UsdPrim const& prim,
793             query_type const* query)
794     {
795         // No attribute defined means inherited, means refer to the parent.
796         // Any defined attribute overrides parent opinion.
797         // If the drawMode is inherited all the way to the root of the scene,
798         // that means "default".
799         value_type v = UsdGeomTokens->inherited;
800         if (*query) {
801             query->Get(&v);
802         }
803         if (v != UsdGeomTokens->inherited) {
804             return v;
805         }
806         v = *owner->_GetValue(prim.GetParent());
807         if (v == UsdGeomTokens->inherited) {
808             return UsdGeomTokens->default_;
809         }
810         return v;
811     }
812 
813     static
814     value_type
ComputeDrawModeUsdImaging_DrawModeStrategy815     ComputeDrawMode(UsdPrim const& prim)
816     {
817         return UsdGeomModelAPI(prim).ComputeModelDrawMode();
818     }
819 };
820 
821 PXR_NAMESPACE_CLOSE_SCOPE
822 
823 // -------------------------------------------------------------------------- //
824 // UsdGeomPointInstancer indices cache
825 // -------------------------------------------------------------------------- //
826 
827 #include "pxr/usd/usdGeom/pointInstancer.h"
828 
829 PXR_NAMESPACE_OPEN_SCOPE
830 
831 struct UsdImaging_PointInstancerIndicesStrategy;
832 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_PointInstancerIndicesStrategy>
833     UsdImaging_PointInstancerIndicesCache;
834 
835 struct UsdImaging_PointInstancerIndicesStrategy
836 {
837     // map from protoIndex -> instanceIndices.
838     typedef VtArray<VtIntArray> value_type;
839     // We don't use query_type, but can't set it to void.
840     typedef int query_type;
841 
842     // XXX: Most indices values will be static, but since they *can*
843     // be animated, we need to return true here to get invalidation on
844     // time-change.  It would be nice to add a per-entry time-varying bit
845     // to the resolved cache, instead of having the global per-attribute
846     // bit.
847     //
848     // In this particular case, instance indices are only recomputed when
849     // we see "DirtyInstanceIndex" in UpdateForTime, so though we'll be
850     // clearing cache entries out of the resolved cache on time-change,
851     // we won't actually call out to the attribute cache on static indices.
852     static
ValueMightBeTimeVaryingUsdImaging_PointInstancerIndicesStrategy853     bool ValueMightBeTimeVarying() { return true; }
854     static
MakeDefaultUsdImaging_PointInstancerIndicesStrategy855     value_type MakeDefault() { return value_type(); }
856 
857     static
MakeQueryUsdImaging_PointInstancerIndicesStrategy858     query_type MakeQuery(UsdPrim const& prim, bool *) {
859         return 0;
860     }
861 
862     static
863     value_type
ComputeUsdImaging_PointInstancerIndicesStrategy864     Compute(UsdImaging_PointInstancerIndicesCache const* owner,
865             UsdPrim const& prim,
866             query_type const* query)
867     {
868         return ComputePerPrototypeIndices(prim, owner->GetTime());
869     }
870 
871     static
872     value_type
ComputePerPrototypeIndicesUsdImaging_PointInstancerIndicesStrategy873     ComputePerPrototypeIndices(UsdPrim const& prim, UsdTimeCode time)
874     {
875         value_type v;
876 
877         UsdGeomPointInstancer pi(prim);
878         VtIntArray protoIndices;
879         if (!pi.GetProtoIndicesAttr().Get(&protoIndices, time)) {
880             TF_WARN("Failed to read point instancer protoIndices");
881             return v;
882         }
883 
884         std::vector<bool> mask = pi.ComputeMaskAtTime(time);
885 
886         for (size_t instanceId = 0; instanceId < protoIndices.size(); ++instanceId) {
887             size_t protoIndex = protoIndices[instanceId];
888 
889             if (protoIndex >= v.size()) {
890                 v.resize(protoIndex + 1);
891             }
892 
893             if (mask.size() == 0 || mask[instanceId]) {
894                 v[protoIndex].push_back(instanceId);
895             }
896         }
897 
898         return v;
899     }
900 };
901 
902 PXR_NAMESPACE_CLOSE_SCOPE
903 
904 // -------------------------------------------------------------------------- //
905 // CoordSysBinding Cache
906 // -------------------------------------------------------------------------- //
907 
908 #include "pxr/usd/usdShade/coordSysAPI.h"
909 #include "pxr/imaging/hd/coordSys.h"
910 
911 PXR_NAMESPACE_OPEN_SCOPE
912 
913 struct UsdImaging_CoordSysBindingImplData {
914     // Helper provided by the scene delegate to pre-convert
915     // the binding paths to the equivalent Hydra ID.
916     std::function<SdfPath(SdfPath)> usdToHydraPath;
917 };
918 
919 struct UsdImaging_CoordSysBindingStrategy;
920 
921 typedef UsdImaging_ResolvedAttributeCache<
922     UsdImaging_CoordSysBindingStrategy,
923     UsdImaging_CoordSysBindingImplData>
924     UsdImaging_CoordSysBindingCache;
925 
926 struct UsdImaging_CoordSysBindingStrategy
927 {
928     using ImplData = UsdImaging_CoordSysBindingImplData;
929 
930     typedef std::vector<UsdShadeCoordSysAPI::Binding> UsdBindingVec;
931     typedef std::shared_ptr<UsdBindingVec> UsdBindingVecPtr;
932     typedef std::shared_ptr<SdfPathVector> IdVecPtr;
933 
934     struct value_type {
935         IdVecPtr idVecPtr;
936         UsdBindingVecPtr usdBindingVecPtr;
937     };
938     struct query_type {
939         UsdShadeCoordSysAPI coordSysAPI;
940         ImplData *implData;
941 
942         // Convert a USD binding relationship to a Hydra ID
943         SdfPath
_IdForBindingUsdImaging_CoordSysBindingStrategy::query_type944         _IdForBinding(UsdShadeCoordSysAPI::Binding const& binding) const {
945             return implData->usdToHydraPath(binding.bindingRelPath);
946         }
947     };
948 
949     static
ValueMightBeTimeVaryingUsdImaging_CoordSysBindingStrategy950     bool ValueMightBeTimeVarying() { return false; }
951 
952     static
MakeDefaultUsdImaging_CoordSysBindingStrategy953     value_type MakeDefault() {
954         return value_type();
955     }
956 
957     static
MakeQueryUsdImaging_CoordSysBindingStrategy958     query_type MakeQuery(UsdPrim const& prim, ImplData *implData) {
959         return query_type({ UsdShadeCoordSysAPI(prim), implData });
960     }
961 
962     static
963     value_type
ComputeUsdImaging_CoordSysBindingStrategy964     Compute(UsdImaging_CoordSysBindingCache const* owner,
965             UsdPrim const& prim,
966             query_type const* query)
967     {
968         value_type v;
969         if (query->coordSysAPI) {
970             // Pull inherited bindings first.
971             if (UsdPrim parentPrim = prim.GetParent()) {
972                 v = *owner->_GetValue(parentPrim);
973             }
974             // Merge any local bindings.
975             if (query->coordSysAPI.HasLocalBindings()) {
976                 SdfPathVector hdIds;
977                 UsdBindingVec usdBindings;
978                 if (v.idVecPtr) {
979                     hdIds = *v.idVecPtr;
980                 }
981                 if (v.usdBindingVecPtr) {
982                     usdBindings = *v.usdBindingVecPtr;
983                 }
984                 for (auto const& binding:
985                      query->coordSysAPI.GetLocalBindings()) {
986                     if (!prim.GetStage()->GetPrimAtPath(
987                         binding.coordSysPrimPath).IsValid()) {
988                         // The target xform prim does not exist, so ignore
989                         // this coord sys binding.
990                         TF_WARN("UsdImaging: Ignoring coordinate system "
991                                 "binding to non-existent prim <%s>\n",
992                                 binding.coordSysPrimPath.GetText());
993                         continue;
994                     }
995                     bool found = false;
996                     for (size_t i=0, n=hdIds.size(); i<n; ++i) {
997                         if (usdBindings[i].name == binding.name) {
998                             // Found an override -- replace this binding.
999                             usdBindings[i] = binding;
1000                             hdIds[i] = query->_IdForBinding(binding);
1001                             found = true;
1002                             break;
1003                         }
1004                     }
1005                     if (!found) {
1006                         // New binding, so append.
1007                         usdBindings.push_back(binding);
1008                         hdIds.push_back(query->_IdForBinding(binding));
1009                     }
1010                 }
1011                 v.idVecPtr.reset(new SdfPathVector(hdIds));
1012                 v.usdBindingVecPtr.reset(new UsdBindingVec(usdBindings));
1013             }
1014         }
1015         return v;
1016     }
1017 };
1018 
1019 PXR_NAMESPACE_CLOSE_SCOPE
1020 
1021 // -------------------------------------------------------------------------- //
1022 // Inherited Primvar Cache
1023 // -------------------------------------------------------------------------- //
1024 
1025 #include "pxr/usd/usdGeom/primvarsAPI.h"
1026 
1027 PXR_NAMESPACE_OPEN_SCOPE
1028 
1029 struct UsdImaging_InheritedPrimvarStrategy;
1030 typedef UsdImaging_ResolvedAttributeCache<UsdImaging_InheritedPrimvarStrategy>
1031     UsdImaging_InheritedPrimvarCache;
1032 
1033 struct UsdImaging_InheritedPrimvarStrategy
1034 {
1035     struct PrimvarRecord {
1036         std::vector<UsdGeomPrimvar> primvars;
1037         bool variable;
1038     };
1039     typedef std::shared_ptr<PrimvarRecord> value_type;
1040     typedef UsdGeomPrimvarsAPI query_type;
1041 
1042     // While primvar data might be time-varying, the set of primvars applying
1043     // to a prim will not.
1044     static
ValueMightBeTimeVaryingUsdImaging_InheritedPrimvarStrategy1045     bool ValueMightBeTimeVarying() { return false; }
1046 
1047     static
MakeDefaultUsdImaging_InheritedPrimvarStrategy1048     value_type MakeDefault() {
1049         return value_type();
1050     }
1051 
1052     static
MakeQueryUsdImaging_InheritedPrimvarStrategy1053     query_type MakeQuery(UsdPrim const& prim, bool *) {
1054         return query_type(UsdGeomPrimvarsAPI(prim));
1055     }
1056 
1057     static
ComputeUsdImaging_InheritedPrimvarStrategy1058     value_type Compute(UsdImaging_InheritedPrimvarCache const* owner,
1059                        UsdPrim const& prim,
1060                        query_type const* query)
1061     {
1062         value_type v;
1063         if (*query) {
1064             // Pull inherited bindings first.
1065             if (UsdPrim parentPrim = prim.GetParent()) {
1066                 v = *owner->_GetValue(parentPrim);
1067             }
1068             // Merge any local bindings.
1069             std::vector<UsdGeomPrimvar> primvars =
1070                 query->FindIncrementallyInheritablePrimvars(
1071                     v ? v->primvars : std::vector<UsdGeomPrimvar>());
1072             if (!primvars.empty()) {
1073                 v = std::make_shared<PrimvarRecord>();
1074                 v->primvars = std::move(primvars);
1075                 v->variable = false;
1076                 for (UsdGeomPrimvar const& pv : v->primvars) {
1077                     if (pv.ValueMightBeTimeVarying()) {
1078                         v->variable = true;
1079                         break;
1080                     }
1081                 }
1082             }
1083         }
1084         return v;
1085     }
1086 };
1087 
1088 PXR_NAMESPACE_CLOSE_SCOPE
1089 
1090 #endif
1091