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