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