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 #include "pxr/imaging/hd/changeTracker.h"
25 
26 #include "pxr/imaging/hd/perfLog.h"
27 #include "pxr/imaging/hd/renderIndex.h"
28 #include "pxr/imaging/hd/tokens.h"
29 
30 #include "pxr/imaging/hd/dataSourceLocator.h"
31 #include "pxr/imaging/hd/dirtyBitsTranslator.h"
32 #include "pxr/imaging/hd/sceneIndex.h"
33 
34 #include "pxr/base/tf/debug.h"
35 #include "pxr/base/tf/token.h"
36 
37 #include <iostream>
38 #include <sstream>
39 
40 PXR_NAMESPACE_OPEN_SCOPE
41 
HdChangeTracker()42 HdChangeTracker::HdChangeTracker()
43     : _rprimState()
44     , _instancerState()
45     , _taskState()
46     , _sprimState()
47     , _bprimState()
48     , _generalState()
49     , _collectionState()
50     , _instancerRprimDependencies()
51     , _instancerInstancerDependencies()
52     , _sprimSprimTargetDependencies()
53     , _sprimSprimSourceDependencies()
54     // Note: Version numbers start at 1, with observers resetting theirs to 0.
55     // This is to cause a version mismatch during first-time processing.
56     , _varyingStateVersion(1)
57     , _rprimIndexVersion(1)
58     , _sprimIndexVersion(1)
59     , _bprimIndexVersion(1)
60     , _instancerIndexVersion(1)
61     , _sceneStateVersion(1)
62     , _visChangeCount(1)
63     , _rprimRenderTagVersion(1)
64     , _taskRenderTagsVersion(1)
65     , _emulationSceneIndex(nullptr)
66 {
67     /*NOTHING*/
68 }
69 
~HdChangeTracker()70 HdChangeTracker::~HdChangeTracker()
71 {
72     HD_TRACE_FUNCTION();
73     HF_MALLOC_TAG_FUNCTION();
74 }
75 
76 void
_LogCacheAccess(TfToken const & cacheName,SdfPath const & id,bool hit)77 HdChangeTracker::_LogCacheAccess(TfToken const& cacheName,
78                                  SdfPath const& id,
79                                  bool hit)
80 {
81     if (hit) {
82         HD_PERF_CACHE_HIT(cacheName, id);
83     } else {
84         HD_PERF_CACHE_MISS(cacheName, id);
85     }
86 }
87 
88 void
RprimInserted(SdfPath const & id,HdDirtyBits initialDirtyState)89 HdChangeTracker::RprimInserted(SdfPath const& id, HdDirtyBits initialDirtyState)
90 {
91     TF_DEBUG(HD_RPRIM_ADDED).Msg("Rprim Added: %s\n", id.GetText());
92     _rprimState[id] = initialDirtyState;
93 
94     ++_sceneStateVersion;
95     ++_rprimIndexVersion;
96 }
97 
98 void
RprimRemoved(SdfPath const & id)99 HdChangeTracker::RprimRemoved(SdfPath const& id)
100 {
101     TF_DEBUG(HD_RPRIM_REMOVED).Msg("Rprim Removed: %s\n", id.GetText());
102     _rprimState.erase(id);
103 
104     ++_sceneStateVersion;
105     ++_rprimIndexVersion;
106 }
107 
108 void
MarkRprimDirty(SdfPath const & id,HdDirtyBits bits)109 HdChangeTracker::MarkRprimDirty(SdfPath const& id, HdDirtyBits bits)
110 {
111     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
112         TF_CODING_ERROR("MarkRprimDirty called with bits == clean!");
113         return;
114     }
115 
116     if (_emulationSceneIndex) {
117 
118         // There's a set of dirty bits that are used as internal signalling
119         // in hydra, and aren't related to scene data.  These, we need to
120         // pass through directly.
121         const HdDirtyBits internalDirtyBits =
122             HdChangeTracker::InitRepr |
123             HdChangeTracker::Varying |
124             HdChangeTracker::NewRepr |
125             HdChangeTracker::CustomBitsMask;
126 
127         if (bits & internalDirtyBits) {
128             _MarkRprimDirty(id, bits & internalDirtyBits);
129         }
130 
131          // If we're only processing internal bits, skip calling DirtyPrims.
132         if ((bits & ~internalDirtyBits) == 0) {
133             return;
134         }
135 
136         // We need to dispatch based on prim type.
137         HdSceneIndexPrim prim = _emulationSceneIndex->GetPrim(id);
138         HdDataSourceLocatorSet locators;
139         HdDirtyBitsTranslator::RprimDirtyBitsToLocatorSet(
140                 prim.primType, bits, &locators);
141         if (!locators.IsEmpty()) {
142             _emulationSceneIndex->DirtyPrims({{id, locators}});
143         }
144     } else {
145         // XXX: During the migration, "DirtyPrimvar" implies DirtyPoints/etc.
146         if (bits & DirtyPrimvar) {
147             bits |= DirtyPoints | DirtyNormals | DirtyWidths;
148         }
149         _MarkRprimDirty(id, bits);
150     }
151 }
152 
_MarkRprimDirty(SdfPath const & id,HdDirtyBits bits)153 void HdChangeTracker::_MarkRprimDirty(SdfPath const& id, HdDirtyBits bits)
154 {
155     _IDStateMap::iterator it = _rprimState.find(id);
156     if (!TF_VERIFY(it != _rprimState.end(), "%s\n", id.GetText())) {
157         return;
158     }
159 
160     // Early out if no new bits are being set.
161     if ((bits & (~it->second)) == 0) {
162         // Can not early out if the change represents
163         // a change to the prim filter.  These need to
164         // trigger a re-evaluation of the dirty list so need
165         // certain version flags to be incremented.
166         // These may not be marked clean if the prim
167         // is filtered out, so don't early out!
168         if ((bits & (DirtyRenderTag | DirtyRepr)) == 0) {
169             return;
170         }
171     }
172 
173     // Used to ensure the repr has been created. Don't touch scene state version
174     if (bits == HdChangeTracker::InitRepr) {
175         it->second |= HdChangeTracker::InitRepr;
176         return;
177     }
178 
179     // set Varying bit if it's not set
180     HdDirtyBits oldBits = it->second;
181     if ((oldBits & HdChangeTracker::Varying) == 0) {
182         TF_DEBUG(HD_VARYING_STATE).Msg("New Varying State %s: %s\n",
183                                        id.GetText(),
184                                        StringifyDirtyBits(bits).c_str());
185 
186         // varying state changed.
187         bits |= HdChangeTracker::Varying;
188         ++_varyingStateVersion;
189     }
190     it->second = oldBits | bits;
191     ++_sceneStateVersion;
192 
193     if ((bits & DirtyVisibility) != 0) {
194         ++_visChangeCount;
195     }
196 
197     if ((bits & DirtyRenderTag) != 0) {
198         ++_rprimRenderTagVersion;
199     }
200 
201     if ((bits & (DirtyRenderTag | DirtyRepr)) != 0) {
202         // Need to treat these like a scene edits
203         // For Render Tag
204         //  - DirtyLists will filter out prims that don't match render tag,
205         //  - Batches filter out prim that don't match render tag,
206         // With Repr, it may require the new repr to be initialized
207         //  - DirtyLists manages repr initialization
208         //  - Batches gather only draw items that match the repr.
209         // So both need to be rebuilt.
210         // So increment the render index version.
211         ++_rprimIndexVersion;
212     }
213 }
214 
215 void
ResetVaryingState()216 HdChangeTracker::ResetVaryingState()
217 {
218     TRACE_FUNCTION();
219 
220     TF_DEBUG(HD_VARYING_STATE).Msg(
221         "Resetting Rprim Varying State: varyingStateVersion (%d -> %d)\n",
222         _varyingStateVersion, _varyingStateVersion + 1);
223 
224     ++_varyingStateVersion;
225 
226     // reset all variability bit
227     TF_FOR_ALL (it, _rprimState) {
228         if (IsClean(it->second)) {
229             it->second &= ~Varying;
230         }
231     }
232 }
233 
234 void
ResetRprimVaryingState(SdfPath const & id)235 HdChangeTracker::ResetRprimVaryingState(SdfPath const& id)
236 {
237     TF_DEBUG(HD_VARYING_STATE).Msg("Resetting Rprim Varying State: %s\n",
238                                    id.GetText());
239 
240     _IDStateMap::iterator it = _rprimState.find(id);
241     if (!TF_VERIFY(it != _rprimState.end(), "%s\n", id.GetText())) {
242         return;
243     }
244 
245     // Don't update varying state or change count as we don't want to
246     // cause re-evaluation of the varying state now, but
247     // want to pick up the possible change on the next iteration.
248 
249     it->second &= ~Varying;
250 }
251 
252 void
MarkRprimClean(SdfPath const & id,HdDirtyBits newBits)253 HdChangeTracker::MarkRprimClean(SdfPath const& id, HdDirtyBits newBits)
254 {
255     TF_DEBUG(HD_RPRIM_CLEANED).Msg("Rprim Cleaned: %s\n", id.GetText());
256     _IDStateMap::iterator it = _rprimState.find(id);
257     if (!TF_VERIFY(it != _rprimState.end()))
258         return;
259     // preserve the variability bit
260     it->second = (it->second & Varying) | newBits;
261 }
262 
263 void
InstancerInserted(SdfPath const & id,HdDirtyBits initialDirtyState)264 HdChangeTracker::InstancerInserted(SdfPath const& id, HdDirtyBits initialDirtyState)
265 {
266     TF_DEBUG(HD_INSTANCER_ADDED).Msg("Instancer Added: %s\n", id.GetText());
267     _instancerState[id] = initialDirtyState;
268     ++_sceneStateVersion;
269     ++_instancerIndexVersion;
270 }
271 
272 void
InstancerRemoved(SdfPath const & id)273 HdChangeTracker::InstancerRemoved(SdfPath const& id)
274 {
275     TF_DEBUG(HD_INSTANCER_REMOVED).Msg("Instancer Removed: %s\n", id.GetText());
276     _instancerState.erase(id);
277     ++_sceneStateVersion;
278     ++_instancerIndexVersion;
279 }
280 
281 
282 // -------------------------------------------------------------------------- //
283 /// \name Dependency Tracking
284 // -------------------------------------------------------------------------- //
285 
286 void
AddInstancerRprimDependency(SdfPath const & instancerId,SdfPath const & rprimId)287 HdChangeTracker::AddInstancerRprimDependency(SdfPath const& instancerId,
288                                              SdfPath const& rprimId)
289 {
290     _AddDependency(_instancerRprimDependencies, instancerId, rprimId);
291 }
292 
293 void
RemoveInstancerRprimDependency(SdfPath const & instancerId,SdfPath const & rprimId)294 HdChangeTracker::RemoveInstancerRprimDependency(SdfPath const& instancerId,
295                                                 SdfPath const& rprimId)
296 {
297     _RemoveDependency(_instancerRprimDependencies, instancerId, rprimId);
298 }
299 
300 void
AddInstancerInstancerDependency(SdfPath const & parentId,SdfPath const & instancerId)301 HdChangeTracker::AddInstancerInstancerDependency(SdfPath const& parentId,
302                                                  SdfPath const& instancerId)
303 {
304     _AddDependency(_instancerInstancerDependencies, parentId, instancerId);
305 }
306 
307 void
RemoveInstancerInstancerDependency(SdfPath const & parentId,SdfPath const & instancerId)308 HdChangeTracker::RemoveInstancerInstancerDependency(SdfPath const& parentId,
309                                                     SdfPath const& instancerId)
310 {
311     _RemoveDependency(_instancerInstancerDependencies, parentId, instancerId);
312 }
313 
314 void
AddSprimSprimDependency(SdfPath const & parentSprimId,SdfPath const & sprimId)315 HdChangeTracker::AddSprimSprimDependency(SdfPath const& parentSprimId,
316                                          SdfPath const& sprimId)
317 {
318     _AddDependency(_sprimSprimTargetDependencies, parentSprimId, sprimId);
319     _AddDependency(_sprimSprimSourceDependencies, sprimId, parentSprimId);
320 }
321 
322 void
RemoveSprimSprimDependency(SdfPath const & parentSprimId,SdfPath const & sprimId)323 HdChangeTracker::RemoveSprimSprimDependency(SdfPath const& parentSprimId,
324                                             SdfPath const& sprimId)
325 {
326     _RemoveDependency(_sprimSprimTargetDependencies, parentSprimId, sprimId);
327     _RemoveDependency(_sprimSprimSourceDependencies, sprimId, parentSprimId);
328 }
329 
330 void
RemoveSprimFromSprimSprimDependencies(SdfPath const & sprimId)331 HdChangeTracker::RemoveSprimFromSprimSprimDependencies(SdfPath const& sprimId)
332 {
333     if (_sprimSprimTargetDependencies.empty()) {
334         return;
335     }
336 
337     // Remove sprim as parent
338     {
339         _DependencyMap::accessor a;
340         if (_sprimSprimTargetDependencies.find(a, sprimId)) {
341             // If sprim is parent, mark its children dirty before removing.
342             for (const SdfPath & childPath : a->second) {
343                 MarkSprimDirty(childPath, ~Clean);
344                 _RemoveDependency(
345                     _sprimSprimSourceDependencies, childPath, sprimId);
346             }
347             _sprimSprimTargetDependencies.erase(a);
348         }
349     }
350 
351     // Remove sprim as child
352     {
353         _DependencyMap::accessor a;
354         if (_sprimSprimSourceDependencies.find(a, sprimId)) {
355             for (const SdfPath & parentPath : a->second) {
356                 _RemoveDependency(
357                     _sprimSprimTargetDependencies, parentPath, sprimId);
358             }
359             _sprimSprimSourceDependencies.erase(a);
360         }
361     }
362 }
363 
364 void
_AddDependency(HdChangeTracker::_DependencyMap & depMap,SdfPath const & parent,SdfPath const & child)365 HdChangeTracker::_AddDependency(HdChangeTracker::_DependencyMap &depMap,
366         SdfPath const& parent, SdfPath const& child)
367 {
368     _DependencyMap::accessor a;
369     depMap.insert(a, parent);
370     a->second.insert(child);
371 }
372 
373 void
_RemoveDependency(HdChangeTracker::_DependencyMap & depMap,SdfPath const & parent,SdfPath const & child)374 HdChangeTracker::_RemoveDependency(HdChangeTracker::_DependencyMap &depMap,
375         SdfPath const& parent, SdfPath const& child)
376 {
377     _DependencyMap::accessor a;
378     if(!depMap.find(a, parent)) {
379         return;
380     }
381     a->second.erase(child);
382     if (a->second.empty()) {
383         depMap.erase(a);
384     }
385 }
386 
387 // -------------------------------------------------------------------------- //
388 /// \name Task Object Tracking
389 // -------------------------------------------------------------------------- //
390 
391 void
TaskInserted(SdfPath const & id,HdDirtyBits initialDirtyState)392 HdChangeTracker::TaskInserted(SdfPath const& id, HdDirtyBits initialDirtyState)
393 {
394     TF_DEBUG(HD_TASK_ADDED).Msg("Task Added: %s\n", id.GetText());
395     _taskState[id] = initialDirtyState;
396     ++_sceneStateVersion;
397 }
398 
399 void
TaskRemoved(SdfPath const & id)400 HdChangeTracker::TaskRemoved(SdfPath const& id)
401 {
402     TF_DEBUG(HD_TASK_REMOVED).Msg("Task Removed: %s\n", id.GetText());
403     _taskState.erase(id);
404     ++_sceneStateVersion;
405 }
406 
407 void
MarkTaskDirty(SdfPath const & id,HdDirtyBits bits)408 HdChangeTracker::MarkTaskDirty(SdfPath const& id, HdDirtyBits bits)
409 {
410     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
411         TF_CODING_ERROR("MarkTaskDirty called with bits == clean!");
412         return;
413     }
414 
415     _IDStateMap::iterator it = _taskState.find(id);
416     if (!TF_VERIFY(it != _taskState.end(), "Task Id = %s", id.GetText())) {
417         return;
418     }
419 
420     if ((bits & DirtyRenderTags) && (it->second & DirtyRenderTags) == 0) {
421         ++_taskRenderTagsVersion;
422     }
423 
424     it->second = it->second | bits;
425     ++_sceneStateVersion;
426 }
427 
428 HdDirtyBits
GetTaskDirtyBits(SdfPath const & id)429 HdChangeTracker::GetTaskDirtyBits(SdfPath const& id)
430 {
431     _IDStateMap::iterator it = _taskState.find(id);
432     if (!TF_VERIFY(it != _taskState.end()))
433         return Clean;
434     return it->second;
435 }
436 
437 void
MarkTaskClean(SdfPath const & id,HdDirtyBits newBits)438 HdChangeTracker::MarkTaskClean(SdfPath const& id, HdDirtyBits newBits)
439 {
440     _IDStateMap::iterator it = _taskState.find(id);
441     if (!TF_VERIFY(it != _taskState.end()))
442         return;
443     // preserve the variability bit
444     it->second = (it->second & Varying) | newBits;
445 }
446 
447 unsigned
GetRenderTagVersion() const448 HdChangeTracker::GetRenderTagVersion() const
449 {
450     return _rprimRenderTagVersion;
451 }
452 
453 unsigned
GetTaskRenderTagsVersion() const454 HdChangeTracker::GetTaskRenderTagsVersion() const
455 {
456     return _taskRenderTagsVersion;
457 }
458 
459 // -------------------------------------------------------------------------- //
460 /// \name Instancer State Tracking
461 // -------------------------------------------------------------------------- //
462 
463 HdDirtyBits
GetInstancerDirtyBits(SdfPath const & id)464 HdChangeTracker::GetInstancerDirtyBits(SdfPath const& id)
465 {
466     _IDStateMap::iterator it = _instancerState.find(id);
467     if (!TF_VERIFY(it != _instancerState.end()))
468         return Clean;
469     return it->second;
470 }
471 
472 void
MarkInstancerDirty(SdfPath const & id,HdDirtyBits bits)473 HdChangeTracker::MarkInstancerDirty(SdfPath const& id, HdDirtyBits bits)
474 {
475     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
476         TF_CODING_ERROR("MarkInstancerDirty called with bits == clean!");
477         return;
478     }
479 
480     if (_emulationSceneIndex) {
481         // We need to dispatch based on prim type.
482         HdSceneIndexPrim prim = _emulationSceneIndex->GetPrim(id);
483         HdDataSourceLocatorSet locators;
484         HdDirtyBitsTranslator::InstancerDirtyBitsToLocatorSet(
485                 prim.primType, bits, &locators);
486         if (!locators.IsEmpty()) {
487             _emulationSceneIndex->DirtyPrims({{id, locators}});
488         }
489     } else {
490         _MarkInstancerDirty(id, bits);
491     }
492 }
493 
494 void
_MarkInstancerDirty(SdfPath const & id,HdDirtyBits bits)495 HdChangeTracker::_MarkInstancerDirty(SdfPath const& id, HdDirtyBits bits)
496 {
497     _IDStateMap::iterator it = _instancerState.find(id);
498     if (!TF_VERIFY(it != _instancerState.end()))
499         return;
500 
501     // not calling _PropagateDirtyBits here. Currenly instancer uses
502     // scale, translate, rotate primvars and there's no dependency between them
503     // unlike points and normals on rprim.
504 
505     // Early out if no new bits are being set.
506     if ((bits & (~it->second)) == 0) {
507         return;
508     }
509 
510     it->second = it->second | bits;
511     ++_sceneStateVersion;
512 
513     // We propagate dirty bits as follows:
514     // * -> DirtyInstancer.
515     // DirtyInstanceIndex -> DirtyInstanceIndex.
516     // DirtyTransform -> DirtyTransform.
517     //
518     // Both DirtyInstanceIndex and DirtyTransform are consumed at the rprim
519     // level, so this gives the rprim a signal that upstream instancer data
520     // relevant to transform composition or instance indices has changed.
521     // XXX: The DirtyTransform dependency here is technically an hdSt dependency
522     // and we should find a better way to express it, although it won't harm
523     // other known backends.
524     HdDirtyBits toPropagate = DirtyInstancer;
525     if (bits & DirtyTransform) {
526         toPropagate |= DirtyTransform;
527     }
528     if (bits & DirtyInstanceIndex) {
529         toPropagate |= DirtyInstanceIndex;
530     }
531 
532     // Now mark any associated rprims or instancers dirty.
533     _DependencyMap::const_accessor aII;
534     if (_instancerInstancerDependencies.find(aII, id)) {
535         for (SdfPath const& dep : aII->second) {
536             MarkInstancerDirty(dep, toPropagate);
537         }
538     }
539 
540     _DependencyMap::const_accessor aIR;
541     if (_instancerRprimDependencies.find(aIR, id)) {
542         for (SdfPath const& dep : aIR->second) {
543             MarkRprimDirty(dep, toPropagate);
544         }
545     }
546 }
547 
548 void
MarkInstancerClean(SdfPath const & id,HdDirtyBits newBits)549 HdChangeTracker::MarkInstancerClean(SdfPath const& id, HdDirtyBits newBits)
550 {
551     TF_DEBUG(HD_INSTANCER_CLEANED).Msg("Instancer Cleaned: %s\n", id.GetText());
552     _IDStateMap::iterator it = _instancerState.find(id);
553     if (!TF_VERIFY(it != _instancerState.end()))
554         return;
555     // preserve the variability bit
556     it->second = (it->second & Varying) | newBits;
557 }
558 
559 // -------------------------------------------------------------------------- //
560 /// \name Sprim Tracking (camera, light...)
561 // -------------------------------------------------------------------------- //
562 
563 void
SprimInserted(SdfPath const & id,HdDirtyBits initialDirtyState)564 HdChangeTracker::SprimInserted(SdfPath const& id, HdDirtyBits initialDirtyState)
565 {
566     TF_DEBUG(HD_SPRIM_ADDED).Msg("Sprim Added: %s\n", id.GetText());
567     _sprimState[id] = initialDirtyState;
568     ++_sceneStateVersion;
569     ++_sprimIndexVersion;
570 }
571 
572 void
SprimRemoved(SdfPath const & id)573 HdChangeTracker::SprimRemoved(SdfPath const& id)
574 {
575     TF_DEBUG(HD_SPRIM_REMOVED).Msg("Sprim Removed: %s\n", id.GetText());
576     _sprimState.erase(id);
577     ++_sceneStateVersion;
578     ++_sprimIndexVersion;
579 }
580 
581 HdDirtyBits
GetSprimDirtyBits(SdfPath const & id)582 HdChangeTracker::GetSprimDirtyBits(SdfPath const& id)
583 {
584     _IDStateMap::iterator it = _sprimState.find(id);
585     if (!TF_VERIFY(it != _sprimState.end()))
586         return Clean;
587     return it->second;
588 }
589 
590 void
MarkSprimDirty(SdfPath const & id,HdDirtyBits bits)591 HdChangeTracker::MarkSprimDirty(SdfPath const& id, HdDirtyBits bits)
592 {
593     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
594         TF_CODING_ERROR("MarkSprimDirty called with bits == clean!");
595         return;
596     }
597 
598     if (_emulationSceneIndex) {
599         // We need to dispatch based on prim type.
600         HdSceneIndexPrim prim = _emulationSceneIndex->GetPrim(id);
601         HdDataSourceLocatorSet locators;
602         HdDirtyBitsTranslator::SprimDirtyBitsToLocatorSet(
603                 prim.primType, bits, &locators);
604         if (!locators.IsEmpty()) {
605             _emulationSceneIndex->DirtyPrims({{id, locators}});
606         }
607     } else {
608         _MarkSprimDirty(id, bits);
609     }
610 }
611 
612 void
_MarkSprimDirty(SdfPath const & id,HdDirtyBits bits)613 HdChangeTracker::_MarkSprimDirty(SdfPath const& id, HdDirtyBits bits)
614 {
615     _IDStateMap::iterator it = _sprimState.find(id);
616     if (!TF_VERIFY(it != _sprimState.end()))
617         return;
618     it->second = it->second | bits;
619 
620     // Mark any associated sprims dirty. Unfortunately, we don't know what to
621     // set dirty, so we resort to ~Clean (as we can't use the rprim AllDirty for
622     // sprims).
623     _DependencyMap::const_accessor aIR;
624     if (_sprimSprimTargetDependencies.find(aIR, id)) {
625         for (SdfPath const& dep : aIR->second) {
626             MarkSprimDirty(dep, ~Clean);
627         }
628     }
629 
630     ++_sceneStateVersion;
631 }
632 
633 void
MarkSprimClean(SdfPath const & id,HdDirtyBits newBits)634 HdChangeTracker::MarkSprimClean(SdfPath const& id, HdDirtyBits newBits)
635 {
636     _IDStateMap::iterator it = _sprimState.find(id);
637     if (!TF_VERIFY(it != _sprimState.end()))
638         return;
639     it->second = newBits;
640 }
641 
642 // -------------------------------------------------------------------------- //
643 /// \name Bprim Tracking (texture, buffer...)
644 // -------------------------------------------------------------------------- //
645 
646 void
BprimInserted(SdfPath const & id,HdDirtyBits initialDirtyState)647 HdChangeTracker::BprimInserted(SdfPath const& id, HdDirtyBits initialDirtyState)
648 {
649     TF_DEBUG(HD_BPRIM_ADDED).Msg("Bprim Added: %s\n", id.GetText());
650     _bprimState[id] = initialDirtyState;
651     ++_sceneStateVersion;
652     ++_bprimIndexVersion;
653 }
654 
655 void
BprimRemoved(SdfPath const & id)656 HdChangeTracker::BprimRemoved(SdfPath const& id)
657 {
658     TF_DEBUG(HD_BPRIM_REMOVED).Msg("Bprim Removed: %s\n", id.GetText());
659     _bprimState.erase(id);
660     ++_sceneStateVersion;
661     ++_bprimIndexVersion;
662 }
663 
664 HdDirtyBits
GetBprimDirtyBits(SdfPath const & id)665 HdChangeTracker::GetBprimDirtyBits(SdfPath const& id)
666 {
667     _IDStateMap::iterator it = _bprimState.find(id);
668     if (!TF_VERIFY(it != _bprimState.end()))
669         return Clean;
670     return it->second;
671 }
672 
673 void
MarkBprimDirty(SdfPath const & id,HdDirtyBits bits)674 HdChangeTracker::MarkBprimDirty(SdfPath const& id, HdDirtyBits bits)
675 {
676     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
677         TF_CODING_ERROR("MarkBprimDirty called with bits == clean!");
678         return;
679     }
680 
681     if (_emulationSceneIndex) {
682         // We need to dispatch based on prim type.
683         HdSceneIndexPrim prim = _emulationSceneIndex->GetPrim(id);
684         HdDataSourceLocatorSet locators;
685         HdDirtyBitsTranslator::BprimDirtyBitsToLocatorSet(
686                 prim.primType, bits, &locators);
687         if (!locators.IsEmpty()) {
688             _emulationSceneIndex->DirtyPrims({{id, locators}});
689         }
690     } else {
691         _MarkBprimDirty(id, bits);
692     }
693 }
694 
695 void
_MarkBprimDirty(SdfPath const & id,HdDirtyBits bits)696 HdChangeTracker::_MarkBprimDirty(SdfPath const& id, HdDirtyBits bits)
697 {
698     _IDStateMap::iterator it = _bprimState.find(id);
699     if (!TF_VERIFY(it != _bprimState.end()))
700         return;
701     it->second = it->second | bits;
702     ++_sceneStateVersion;
703 }
704 
705 void
MarkBprimClean(SdfPath const & id,HdDirtyBits newBits)706 HdChangeTracker::MarkBprimClean(SdfPath const& id, HdDirtyBits newBits)
707 {
708     _IDStateMap::iterator it = _bprimState.find(id);
709     if (!TF_VERIFY(it != _bprimState.end()))
710         return;
711     it->second = newBits;
712 }
713 
714 // -------------------------------------------------------------------------- //
715 /// \name RPrim Object Tracking
716 // -------------------------------------------------------------------------- //
717 
718 
719 bool
IsRprimDirty(SdfPath const & id)720 HdChangeTracker::IsRprimDirty(SdfPath const& id)
721 {
722     return IsDirty(GetRprimDirtyBits(id));
723 }
724 
725 bool
IsTopologyDirty(SdfPath const & id)726 HdChangeTracker::IsTopologyDirty(SdfPath const& id)
727 {
728     return IsTopologyDirty(GetRprimDirtyBits(id), id);
729 }
730 
731 bool
IsDoubleSidedDirty(SdfPath const & id)732 HdChangeTracker::IsDoubleSidedDirty(SdfPath const& id)
733 {
734     return IsDoubleSidedDirty(GetRprimDirtyBits(id), id);
735 }
736 
737 bool
IsCullStyleDirty(SdfPath const & id)738 HdChangeTracker::IsCullStyleDirty(SdfPath const& id)
739 {
740     return IsCullStyleDirty(GetRprimDirtyBits(id), id);
741 }
742 
743 bool
IsDisplayStyleDirty(SdfPath const & id)744 HdChangeTracker::IsDisplayStyleDirty(SdfPath const& id)
745 {
746     return IsDisplayStyleDirty(GetRprimDirtyBits(id), id);
747 }
748 
749 bool
IsSubdivTagsDirty(SdfPath const & id)750 HdChangeTracker::IsSubdivTagsDirty(SdfPath const& id)
751 {
752     return IsSubdivTagsDirty(GetRprimDirtyBits(id), id);
753 }
754 
755 bool
IsTransformDirty(SdfPath const & id)756 HdChangeTracker::IsTransformDirty(SdfPath const& id)
757 {
758     return IsTransformDirty(GetRprimDirtyBits(id), id);
759 }
760 
761 bool
IsVisibilityDirty(SdfPath const & id)762 HdChangeTracker::IsVisibilityDirty(SdfPath const& id)
763 {
764     return IsVisibilityDirty(GetRprimDirtyBits(id), id);
765 }
766 
767 bool
IsExtentDirty(SdfPath const & id)768 HdChangeTracker::IsExtentDirty(SdfPath const& id)
769 {
770     return IsExtentDirty(GetRprimDirtyBits(id), id);
771 }
772 
773 bool
IsPrimIdDirty(SdfPath const & id)774 HdChangeTracker::IsPrimIdDirty(SdfPath const& id)
775 {
776     return IsPrimIdDirty(GetRprimDirtyBits(id), id);
777 }
778 
779 bool
IsAnyPrimvarDirty(SdfPath const & id)780 HdChangeTracker::IsAnyPrimvarDirty(SdfPath const &id)
781 {
782     return IsAnyPrimvarDirty(GetRprimDirtyBits(id), id);
783 }
784 
785 bool
IsPrimvarDirty(SdfPath const & id,TfToken const & name)786 HdChangeTracker::IsPrimvarDirty(SdfPath const& id, TfToken const& name)
787 {
788     return IsPrimvarDirty(GetRprimDirtyBits(id), id, name);
789 }
790 
791 /*static*/
792 bool
IsTopologyDirty(HdDirtyBits dirtyBits,SdfPath const & id)793 HdChangeTracker::IsTopologyDirty(HdDirtyBits dirtyBits, SdfPath const& id)
794 {
795     bool isDirty = (dirtyBits & DirtyTopology) != 0;
796     _LogCacheAccess(HdTokens->topology, id, !isDirty);
797     return isDirty;
798 }
799 
800 /*static*/
801 bool
IsDoubleSidedDirty(HdDirtyBits dirtyBits,SdfPath const & id)802 HdChangeTracker::IsDoubleSidedDirty(HdDirtyBits dirtyBits, SdfPath const& id)
803 {
804     bool isDirty = (dirtyBits & DirtyDoubleSided) != 0;
805     _LogCacheAccess(HdTokens->doubleSided, id, !isDirty);
806     return isDirty;
807 }
808 
809 /*static*/
810 bool
IsCullStyleDirty(HdDirtyBits dirtyBits,SdfPath const & id)811 HdChangeTracker::IsCullStyleDirty(HdDirtyBits dirtyBits, SdfPath const& id)
812 {
813     bool isDirty = (dirtyBits & DirtyCullStyle) != 0;
814     _LogCacheAccess(HdTokens->cullStyle, id, !isDirty);
815     return isDirty;
816 }
817 
818 /*static*/
819 bool
IsDisplayStyleDirty(HdDirtyBits dirtyBits,SdfPath const & id)820 HdChangeTracker::IsDisplayStyleDirty(HdDirtyBits dirtyBits, SdfPath const& id)
821 {
822     bool isDirty = (dirtyBits & DirtyDisplayStyle) != 0;
823     _LogCacheAccess(HdTokens->displayStyle, id, !isDirty);
824     return isDirty;
825 }
826 
827 /*static*/
828 bool
IsSubdivTagsDirty(HdDirtyBits dirtyBits,SdfPath const & id)829 HdChangeTracker::IsSubdivTagsDirty(HdDirtyBits dirtyBits, SdfPath const& id)
830 {
831     bool isDirty = (dirtyBits & DirtySubdivTags) != 0;
832     _LogCacheAccess(HdTokens->subdivTags, id, !isDirty);
833     return isDirty;
834 }
835 
836 /*static*/
837 bool
IsTransformDirty(HdDirtyBits dirtyBits,SdfPath const & id)838 HdChangeTracker::IsTransformDirty(HdDirtyBits dirtyBits, SdfPath const& id)
839 {
840     bool isDirty = (dirtyBits & DirtyTransform) != 0;
841     _LogCacheAccess(HdTokens->transform, id, !isDirty);
842     return isDirty;
843 }
844 
845 /*static*/
846 bool
IsVisibilityDirty(HdDirtyBits dirtyBits,SdfPath const & id)847 HdChangeTracker::IsVisibilityDirty(HdDirtyBits dirtyBits, SdfPath const& id)
848 {
849     bool isDirty = (dirtyBits & DirtyVisibility) != 0;
850     _LogCacheAccess(HdTokens->visibility, id, !isDirty);
851     return isDirty;
852 }
853 
854 /*static*/
855 bool
IsExtentDirty(HdDirtyBits dirtyBits,SdfPath const & id)856 HdChangeTracker::IsExtentDirty(HdDirtyBits dirtyBits, SdfPath const& id)
857 {
858     bool isDirty = (dirtyBits & DirtyExtent) != 0;
859     _LogCacheAccess(HdTokens->extent, id, !isDirty);
860     return isDirty;
861 }
862 
863 /*static*/
864 bool
IsPrimIdDirty(HdDirtyBits dirtyBits,SdfPath const & id)865 HdChangeTracker::IsPrimIdDirty(HdDirtyBits dirtyBits, SdfPath const& id)
866 {
867     bool isDirty = (dirtyBits & DirtyPrimID) != 0;
868     _LogCacheAccess(HdTokens->primID, id, !isDirty);
869     return isDirty;
870 }
871 
872 /*static*/
873 bool
IsInstancerDirty(HdDirtyBits dirtyBits,SdfPath const & id)874 HdChangeTracker::IsInstancerDirty(HdDirtyBits dirtyBits, SdfPath const& id)
875 {
876     bool isDirty = (dirtyBits & DirtyInstancer) != 0;
877     _LogCacheAccess(HdInstancerTokens->instancer, id, !isDirty);
878     return isDirty;
879 }
880 
881 /*static*/
882 bool
IsInstanceIndexDirty(HdDirtyBits dirtyBits,SdfPath const & id)883 HdChangeTracker::IsInstanceIndexDirty(HdDirtyBits dirtyBits, SdfPath const& id)
884 {
885     bool isDirty = (dirtyBits & DirtyInstanceIndex) != 0;
886     _LogCacheAccess(HdInstancerTokens->instanceIndices, id, !isDirty);
887     return isDirty;
888 }
889 
890 /*static*/
891 bool
IsAnyPrimvarDirty(HdDirtyBits dirtyBits,SdfPath const & id)892 HdChangeTracker::IsAnyPrimvarDirty(HdDirtyBits dirtyBits, SdfPath const &id)
893 {
894     bool isDirty = (dirtyBits & (DirtyPoints|
895                                  DirtyNormals|
896                                  DirtyWidths|
897                                  DirtyPrimvar)) != 0;
898     _LogCacheAccess(HdTokens->primvar, id, !isDirty);
899     return isDirty;
900 }
901 
902 /*static*/
903 bool
IsPrimvarDirty(HdDirtyBits dirtyBits,SdfPath const & id,TfToken const & name)904 HdChangeTracker::IsPrimvarDirty(HdDirtyBits dirtyBits, SdfPath const& id,
905                                 TfToken const& name)
906 {
907     bool isDirty = false;
908     if (name == HdTokens->points) {
909         isDirty = (dirtyBits & DirtyPoints) != 0;
910     } else if (name == HdTokens->velocities) {
911         isDirty = (dirtyBits & DirtyPoints) != 0;
912     } else if (name == HdTokens->accelerations) {
913         isDirty = (dirtyBits & DirtyPoints) != 0;
914     } else if (name == HdTokens->normals) {
915         isDirty = (dirtyBits & DirtyNormals) != 0;
916     } else if (name == HdTokens->widths) {
917         isDirty = (dirtyBits & DirtyWidths) != 0;
918     } else {
919         isDirty = (dirtyBits & DirtyPrimvar) != 0;
920     }
921     _LogCacheAccess(name, id, !isDirty);
922     return isDirty;
923 }
924 
925 /*static*/
926 bool
IsReprDirty(HdDirtyBits dirtyBits,SdfPath const & id)927 HdChangeTracker::IsReprDirty(HdDirtyBits dirtyBits, SdfPath const &id)
928 {
929     bool isDirty = (dirtyBits & DirtyRepr) != 0;
930     return isDirty;
931 }
932 
933 void
MarkPrimvarDirty(SdfPath const & id,TfToken const & name)934 HdChangeTracker::MarkPrimvarDirty(SdfPath const& id, TfToken const& name)
935 {
936     HdDirtyBits flag = Clean;
937     MarkPrimvarDirty(&flag, name);
938     MarkRprimDirty(id, flag);
939 }
940 
941 
942 void
MarkAllRprimsDirty(HdDirtyBits bits)943 HdChangeTracker::MarkAllRprimsDirty(HdDirtyBits bits)
944 {
945     HD_TRACE_FUNCTION();
946 
947     if (ARCH_UNLIKELY(bits == HdChangeTracker::Clean)) {
948         TF_CODING_ERROR("MarkAllRprimsDirty called with bits == clean!");
949         return;
950     }
951 
952     if (_emulationSceneIndex) {
953         // Since bit -> locator translation is dependent on prim type,
954         // we can't do much better than devolving to MarkRprimDirty.
955         for (_IDStateMap::iterator it  = _rprimState.begin();
956                                    it != _rprimState.end(); ++it) {
957             MarkRprimDirty(it->first, bits);
958         }
959         return;
960     }
961 
962     //
963     // This function runs similar to calling MarkRprimDirty on every prim.
964     // First it checks to see if the request will set any new dirty bits that
965     // are not already set on the prim.  If there are, it will set the new bits
966     // as see if the prim is in the varying state.  If i t is not it will
967     // transition the prim to varying.
968     //
969     // If any prim was transitioned to varying then the varying state version
970     // counter is incremented.
971     //
972     // This complexity is due to some important optimizations.
973     // The main case is dealing with invisible prims, but equally applies
974     // to other cases where dirty bits don't get cleaned during sync.
975     //
976     // For these cases, we want to avoid having the prim in the dirty list
977     // as there would be no work for it to do.  This is done by clearing the
978     // varying flag.  On the flip-side, we want to avoid thrashing the varying
979     // state, so that if the prim has an attribute that is varying, but
980     // it doesn't get cleared, we don't want to set varying on that prim
981     // every frame.
982     //
983 
984     bool varyingStateUpdated = false;
985 
986     for (_IDStateMap::iterator it  = _rprimState.begin();
987                                it != _rprimState.end(); ++it) {
988 
989         HdDirtyBits &rprimDirtyBits = it->second;
990 
991         // If RenderTag or Repr are marked dirty, we always want to update
992         // the varying state (This matches the don't early out condition in
993         // MarkRprim dirty).
994         if ((bits & ((~rprimDirtyBits) | DirtyRenderTag | DirtyRepr)) != 0) {
995             rprimDirtyBits |= bits;
996 
997             if ((rprimDirtyBits & HdChangeTracker::Varying) == 0) {
998                 rprimDirtyBits |= HdChangeTracker::Varying;
999                 varyingStateUpdated = true;
1000             }
1001         }
1002     }
1003 
1004     if (varyingStateUpdated) {
1005         ++_varyingStateVersion;
1006     }
1007 
1008     // These counters get updated every time, even if no prims
1009     // have moved into the dirty state.
1010     ++_sceneStateVersion;
1011     if ((bits & DirtyVisibility) != 0) {
1012         ++_visChangeCount;
1013     }
1014     if ((bits & DirtyRenderTag) != 0) {
1015         ++_rprimRenderTagVersion;
1016     }
1017     if ((bits & (DirtyRenderTag | DirtyRepr)) != 0) {
1018         // Render tags affect dirty lists and batching, so they need to be
1019         // treated like a scene edit: see comment in MarkRprimDirty.
1020         ++_rprimIndexVersion;
1021     }
1022 }
1023 
1024 
1025 /*static*/
1026 void
MarkPrimvarDirty(HdDirtyBits * dirtyBits,TfToken const & name)1027 HdChangeTracker::MarkPrimvarDirty(HdDirtyBits *dirtyBits, TfToken const &name)
1028 {
1029     HdDirtyBits setBits = Clean;
1030     if (name == HdTokens->points) {
1031         setBits = DirtyPoints;
1032     } else if (name == HdTokens->normals) {
1033         setBits = DirtyNormals;
1034     } else if (name == HdTokens->widths) {
1035         setBits = DirtyWidths;
1036     } else {
1037         setBits = DirtyPrimvar;
1038     }
1039     *dirtyBits |= setBits;
1040 }
1041 
1042 HdDirtyBits
GetRprimDirtyBits(SdfPath const & id) const1043 HdChangeTracker::GetRprimDirtyBits(SdfPath const& id) const
1044 {
1045     _IDStateMap::const_iterator it = _rprimState.find(id);
1046     if (!TF_VERIFY(it != _rprimState.end()))
1047         return Clean;
1048 
1049     // not masking the varying bit, since we use that bit
1050     // in HdRenderIndex::GetDelegateIDsWithDirtyRprims to extract
1051     // all varying rprims.
1052     return it->second;// & (~Varying);
1053 }
1054 
1055 void
AddCollection(TfToken const & collectionName)1056 HdChangeTracker::AddCollection(TfToken const& collectionName)
1057 {
1058     HD_TRACE_FUNCTION();
1059 
1060     _CollectionStateMap::iterator it = _collectionState.find(collectionName);
1061     // if it already exists, just return.
1062     if (it != _collectionState.end()) {
1063         return;
1064     }
1065     _collectionState[collectionName] = 1;
1066 }
1067 
1068 void
MarkCollectionDirty(TfToken const & collectionName)1069 HdChangeTracker::MarkCollectionDirty(TfToken const& collectionName)
1070 {
1071     HD_TRACE_FUNCTION();
1072 
1073     _CollectionStateMap::iterator it = _collectionState.find(collectionName);
1074     if (!TF_VERIFY(it != _collectionState.end(),
1075                       "Collection %s not found\n", collectionName.GetText())) {
1076         return;
1077     }
1078     // bump the version number
1079     it->second += 1;
1080 
1081     ++_sceneStateVersion;
1082 }
1083 
1084 unsigned
GetCollectionVersion(TfToken const & collectionName) const1085 HdChangeTracker::GetCollectionVersion(TfToken const& collectionName) const
1086 {
1087     _CollectionStateMap::const_iterator it = _collectionState.find(collectionName);
1088     if (!(it != _collectionState.end())) {
1089         TF_CODING_ERROR("Change Tracker unable to find collection %s",
1090                         collectionName.GetText());
1091         return _rprimIndexVersion;
1092     }
1093     return it->second + _rprimIndexVersion;
1094 }
1095 
1096 unsigned
GetVisibilityChangeCount() const1097 HdChangeTracker::GetVisibilityChangeCount() const
1098 {
1099     return _visChangeCount;
1100 }
1101 
1102 void
AddState(TfToken const & name)1103 HdChangeTracker::AddState(TfToken const& name)
1104 {
1105     _GeneralStateMap::iterator it = _generalState.find(name);
1106     if (it != _generalState.end()) {
1107         // mark state dirty
1108         ++it->second;
1109     } else {
1110         _generalState[name] = 1;
1111     }
1112 }
1113 
1114 void
MarkStateDirty(TfToken const & name)1115 HdChangeTracker::MarkStateDirty(TfToken const& name)
1116 {
1117     _GeneralStateMap::iterator it = _generalState.find(name);
1118     if (it != _generalState.end()) {
1119         ++it->second;
1120     } else {
1121         TF_CODING_ERROR("Change Tracker unable to find state %s",
1122                         name.GetText());
1123     }
1124 }
1125 
1126 unsigned
GetStateVersion(TfToken const & name) const1127 HdChangeTracker::GetStateVersion(TfToken const &name) const
1128 {
1129     _GeneralStateMap::const_iterator it = _generalState.find(name);
1130     if (it != _generalState.end()) {
1131         return it->second;
1132     } else {
1133         TF_CODING_ERROR("Change Tracker unable to find state %s",
1134                         name.GetText());
1135         return 0;
1136     }
1137 }
1138 
1139 /*static*/
1140 std::string
StringifyDirtyBits(HdDirtyBits dirtyBits)1141 HdChangeTracker::StringifyDirtyBits(HdDirtyBits dirtyBits)
1142 {
1143     if (dirtyBits == HdChangeTracker::Clean) {
1144         return std::string("Clean");
1145     }
1146 
1147     std::stringstream ss;
1148 
1149     if (dirtyBits & Varying) {
1150         ss << "<Varying> ";
1151     }
1152     if (dirtyBits & InitRepr) {
1153         ss << "<InitRepr> ";
1154     }
1155     if (dirtyBits & DirtyPrimID) {
1156         ss << " PrimID ";
1157     }
1158     if (dirtyBits & DirtyExtent) {
1159         ss << "Extent ";
1160     }
1161     if (dirtyBits & DirtyDisplayStyle) {
1162         ss << "DisplayStyle ";
1163     }
1164     if (dirtyBits & DirtyPoints) {
1165         ss << "Points ";
1166     }
1167     if (dirtyBits & DirtyPrimvar) {
1168         ss << "Primvar ";
1169     }
1170     if (dirtyBits & DirtyMaterialId) {
1171         ss << "MaterialId ";
1172     }
1173     if (dirtyBits & DirtyTopology) {
1174         ss << "Topology ";
1175     }
1176     if (dirtyBits & DirtyTransform) {
1177         ss << "Transform ";
1178     }
1179     if (dirtyBits & DirtyVisibility) {
1180         ss << "Visibility ";
1181     }
1182     if (dirtyBits & DirtyNormals) {
1183         ss << "Normals ";
1184     }
1185     if (dirtyBits & DirtyDoubleSided) {
1186         ss << "DoubleSided ";
1187     }
1188     if (dirtyBits & DirtyCullStyle) {
1189         ss << "CullStyle ";
1190     }
1191     if (dirtyBits & DirtySubdivTags) {
1192         ss << "SubdivTags ";
1193     }
1194     if (dirtyBits & DirtyWidths) {
1195         ss << "Widths ";
1196     }
1197     if (dirtyBits & DirtyInstancer) {
1198         ss << "Instancer ";
1199     }
1200     if (dirtyBits & DirtyInstanceIndex) {
1201         ss << "InstanceIndex ";
1202     }
1203     if (dirtyBits & DirtyRepr) {
1204         ss << "Repr ";
1205     }
1206     if (dirtyBits & DirtyRenderTag) {
1207         ss << "RenderTag ";
1208     }
1209     if (dirtyBits & DirtyComputationPrimvarDesc) {
1210         ss << "ComputationPrimvarDesc ";
1211     }
1212     if (dirtyBits & DirtyCategories) {
1213         ss << "Categories ";
1214     }
1215     if (dirtyBits & DirtyVolumeField) {
1216         ss << "VolumeField ";
1217     }
1218     if (dirtyBits & NewRepr) {
1219         ss << "NewRepr ";
1220     }
1221     if (dirtyBits & ~AllSceneDirtyBits) {
1222         ss << "CustomBits:";
1223         for (size_t i = CustomBitsBegin; i <= CustomBitsEnd; i<<=1) {
1224             ss << ((dirtyBits & i) ? "1" : "0");
1225         }
1226     }
1227     return ss.str();
1228 }
1229 
1230 /*static*/
1231 void
DumpDirtyBits(HdDirtyBits dirtyBits)1232 HdChangeTracker::DumpDirtyBits(HdDirtyBits dirtyBits)
1233 {
1234     std::cerr
1235         << "DirtyBits:"
1236         << HdChangeTracker::StringifyDirtyBits(dirtyBits)
1237         << "\n";
1238 }
1239 
1240 void
_SetTargetSceneIndex(HdRetainedSceneIndex * emulationSceneIndex)1241 HdChangeTracker::_SetTargetSceneIndex(HdRetainedSceneIndex *emulationSceneIndex)
1242 {
1243     _emulationSceneIndex = emulationSceneIndex;
1244 }
1245 
1246 PXR_NAMESPACE_CLOSE_SCOPE
1247 
1248