1 //
2 // Copyright 2019 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/pxr.h"
25 #include "pxr/usd/usd/stageLoadRules.h"
26 #include "pxr/base/tf/diagnostic.h"
27 #include "pxr/base/tf/ostreamMethods.h"
28 #include "pxr/base/tf/registryManager.h"
29 #include "pxr/base/tf/stl.h"
30
31 #include <boost/functional/hash.hpp>
32
33 #include <algorithm>
34
35 PXR_NAMESPACE_OPEN_SCOPE
36
TF_REGISTRY_FUNCTION(TfEnum)37 TF_REGISTRY_FUNCTION(TfEnum)
38 {
39 TF_ADD_ENUM_NAME(UsdStageLoadRules::AllRule);
40 TF_ADD_ENUM_NAME(UsdStageLoadRules::OnlyRule);
41 TF_ADD_ENUM_NAME(UsdStageLoadRules::NoneRule);
42 }
43
44 using _GetPath = TfGet<0>;
45
46 UsdStageLoadRules
LoadNone()47 UsdStageLoadRules::LoadNone()
48 {
49 UsdStageLoadRules ret;
50 ret._rules.emplace_back(SdfPath::AbsoluteRootPath(), NoneRule);
51 return ret;
52 }
53
54 void
LoadWithDescendants(SdfPath const & path)55 UsdStageLoadRules::LoadWithDescendants(SdfPath const &path)
56 {
57 auto range = SdfPathFindPrefixedRange(
58 _rules.begin(), _rules.end(), path, _GetPath());
59
60 _rules.insert(_rules.erase(range.first, range.second), { path, AllRule });
61 }
62
63 void
LoadWithoutDescendants(SdfPath const & path)64 UsdStageLoadRules::LoadWithoutDescendants(SdfPath const &path)
65 {
66 auto range = SdfPathFindPrefixedRange(
67 _rules.begin(), _rules.end(), path, _GetPath());
68
69 _rules.insert(_rules.erase(range.first, range.second), { path, OnlyRule });
70 }
71
72 void
Unload(SdfPath const & path)73 UsdStageLoadRules::Unload(SdfPath const &path)
74 {
75 auto range = SdfPathFindPrefixedRange(
76 _rules.begin(), _rules.end(), path, _GetPath());
77
78 _rules.emplace(_rules.erase(range.first, range.second), path, NoneRule);
79 }
80
81 void
LoadAndUnload(const SdfPathSet & loadSet,const SdfPathSet & unloadSet,UsdLoadPolicy policy)82 UsdStageLoadRules::LoadAndUnload(const SdfPathSet &loadSet,
83 const SdfPathSet &unloadSet,
84 UsdLoadPolicy policy)
85 {
86 // XXX Could potentially be faster...
87 for (SdfPath const &path: unloadSet) {
88 Unload(path);
89 }
90 for (SdfPath const &path: loadSet) {
91 if (policy == UsdLoadWithDescendants) {
92 LoadWithDescendants(path);
93 }
94 else if (policy == UsdLoadWithoutDescendants) {
95 LoadWithoutDescendants(path);
96 }
97 }
98 }
99
100 void
AddRule(SdfPath const & path,Rule rule)101 UsdStageLoadRules::AddRule(SdfPath const &path, Rule rule)
102 {
103 auto iter = _LowerBound(path);
104 if (iter != _rules.end() && iter->first == path) {
105 iter->second = rule;
106 }
107 else {
108 _rules.emplace(iter, path, rule);
109 }
110 }
111
112 void
SetRules(std::vector<std::pair<SdfPath,Rule>> const & rules)113 UsdStageLoadRules::SetRules(std::vector<std::pair<SdfPath, Rule>> const &rules)
114 {
115 _rules = rules;
116 }
117
118 void
Minimize()119 UsdStageLoadRules::Minimize()
120 {
121 if (_rules.empty()) {
122 return;
123 }
124
125 // If there's an 'AllRule' for '/', remove it -- the implicit rule for '/'
126 // with no entry present is already 'AllRule'.
127 if (_rules.front().second == AllRule &&
128 _rules.front().first == SdfPath::AbsoluteRootPath()) {
129 _rules.erase(_rules.begin());
130 }
131
132 if (_rules.size() <= 1) {
133 return;
134 }
135
136 // Walk forward, keeping a stack of "parents" (really nearest ancestors).
137 // Any entry whose next nearest ancestral entry has the same rule can be
138 // removed as redundant.
139 std::vector<size_t> parentIdxStack;
140 SdfPath curPrefix = _rules.front().first;
141 for (size_t i = 0; i != _rules.size(); ++i) {
142 auto const &cur = _rules[i];
143
144 // Pop ancestral rules off the stack until we find the one for cur, or
145 // until there are no more ancestral rules.
146 while (!parentIdxStack.empty() &&
147 !cur.first.HasPrefix(_rules[parentIdxStack.back()].first)) {
148 parentIdxStack.pop_back();
149 }
150
151 // Parent rule is implicitly 'AllRule' if there is no parent.
152 Rule parentRule = parentIdxStack.empty() ?
153 AllRule : _rules[parentIdxStack.back()].second;
154 if (cur.second == parentRule) {
155 // Remove this rule.
156 _rules.erase(_rules.begin() + i);
157 --i;
158 }
159 else {
160 // This rule is kept and becomes the next parent.
161 parentIdxStack.push_back(i);
162 }
163 }
164 }
165
166 bool
IsLoaded(SdfPath const & path) const167 UsdStageLoadRules::IsLoaded(SdfPath const &path) const
168 {
169 return GetEffectiveRuleForPath(path) != NoneRule;
170 }
171
172 bool
IsLoadedWithAllDescendants(SdfPath const & path) const173 UsdStageLoadRules::IsLoadedWithAllDescendants(SdfPath const &path) const
174 {
175 if (_rules.empty()) {
176 // LoadAll case.
177 return true;
178 }
179
180 // Find the longest prefix of \p path. It must be an AllRule.
181 auto prefixIter = SdfPathFindLongestPrefix(
182 _rules.begin(), _rules.end(), path, _GetPath());
183
184 // There must either be no prefix, or a prefix that's AllRule.
185 if (prefixIter != _rules.end() && prefixIter->second != AllRule) {
186 return false;
187 }
188
189 // Find the range of paths prefixed by the given path. There must either be
190 // none, or all of them must be AllRules.
191 auto range = SdfPathFindPrefixedRange(
192 _rules.begin(), _rules.end(), path, _GetPath());
193
194 for (auto iter = range.first; iter != range.second; ++iter) {
195 if (iter->second != AllRule) {
196 return false;
197 }
198 }
199
200 // This path and all descendants are considered loaded.
201 return true;
202 }
203
204 bool
IsLoadedWithNoDescendants(SdfPath const & path) const205 UsdStageLoadRules::IsLoadedWithNoDescendants(SdfPath const &path) const
206 {
207 if (_rules.empty()) {
208 // LoadAll case.
209 return false;
210 }
211
212 // Look for \p path in _rules. It must be present and must be an OnlyRule.
213 auto compareFn = [](std::pair<SdfPath, Rule> const &entry,
214 SdfPath const &p) {
215 return entry.first < p;
216 };
217 auto iter =
218 std::lower_bound(_rules.begin(), _rules.end(), path, compareFn);
219
220 if (iter == _rules.end() ||
221 iter->first != path ||
222 iter->second != OnlyRule) {
223 return false;
224 }
225
226 // Skip the entry for this path, and scan forward to the next non-NoneRule.
227 // If it has this path as prefix, return false, otherwise return true.
228 for (++iter; iter != _rules.end(); ++iter) {
229 if (iter->second != NoneRule) {
230 return !iter->first.HasPrefix(path);
231 }
232 }
233
234 // Encountered only NoneRules: this path is loaded with no descendants.
235 return true;
236 }
237
238 UsdStageLoadRules::Rule
GetEffectiveRuleForPath(SdfPath const & path) const239 UsdStageLoadRules::GetEffectiveRuleForPath(SdfPath const &path) const
240 {
241 if (_rules.empty()) {
242 // LoadAll case.
243 return AllRule;
244 }
245
246 // Find the longest prefix of \p path. If it is an AllRule, or it is an
247 // OnlyRule and its path is the same as this path, then this path is
248 // included.
249 auto prefixIter = SdfPathFindLongestPrefix(
250 _rules.begin(), _rules.end(), path, _GetPath());
251
252 // If no prefix present, this path is included.
253 if (prefixIter == _rules.end()) {
254 return AllRule;
255 }
256
257 // If the prefix path's rule is AllRule, this path is included.
258 if (prefixIter->second == AllRule) {
259 return AllRule;
260 }
261
262 // If the prefix *is* this path and it's OnlyRule, we have the answer in
263 // hand.
264 if (prefixIter->first == path && prefixIter->second == OnlyRule) {
265 return OnlyRule;
266 }
267
268 // Otherwise find all the "direct child"-type paths of \p path. That is,
269 // subsequent paths that are prefixed _by_ \p path, but skipping deeper such
270 // paths. For example, if path is /Foo/Bar, then we want to consider rules
271 // for /Foo/Bar/Baz and /Foo/Bar/Qux, but not /Foo/Bar/Baz/Child. If one of
272 // these exists and it is an AllRule or OnlyRule, then this path is included
273 // as 'OnlyRule', since it's part of the ancestor chain. Otherwise this
274 // path is excluded.
275
276 ++prefixIter;
277 auto range = SdfPathFindPrefixedRange(
278 prefixIter, _rules.end(), path, _GetPath());
279
280 // If there are no such paths, this path is a NoneRule.
281 if (range.first == range.second) {
282 return NoneRule;
283 }
284
285 auto iter = range.first;
286 while (iter != range.second) {
287 if (iter->second == OnlyRule || iter->second == AllRule) {
288 return OnlyRule;
289 }
290 // Skip anything prefixed by iter->first.
291 auto next = iter + 1;
292 while (next != range.second && next->first.HasPrefix(iter->first)) {
293 ++next;
294 }
295 iter = next;
296 }
297 return NoneRule;
298 }
299
300 bool
operator ==(UsdStageLoadRules const & other) const301 UsdStageLoadRules::operator==(UsdStageLoadRules const &other) const {
302 return _rules == other._rules;
303 }
304
305 std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> >::const_iterator
_LowerBound(SdfPath const & path) const306 UsdStageLoadRules::_LowerBound(SdfPath const &path) const
307 {
308 return std::lower_bound(
309 _rules.begin(), _rules.end(), path,
310 [](std::pair<SdfPath, Rule> const &elem, SdfPath const &path) {
311 return elem.first < path;
312 });
313 }
314
315 std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> >::iterator
_LowerBound(SdfPath const & path)316 UsdStageLoadRules::_LowerBound(SdfPath const &path)
317 {
318 return std::lower_bound(
319 _rules.begin(), _rules.end(), path,
320 [](std::pair<SdfPath, Rule> const &elem, SdfPath const &path) {
321 return elem.first < path;
322 });
323 }
324
325 std::ostream &
operator <<(std::ostream & os,std::pair<SdfPath,UsdStageLoadRules::Rule> const & p)326 operator<<(std::ostream &os,
327 std::pair<SdfPath, UsdStageLoadRules::Rule> const &p) {
328 return os << "(<" << p.first << ">, " <<
329 (p.second == UsdStageLoadRules::AllRule ? "AllRule" :
330 p.second == UsdStageLoadRules::OnlyRule ? "OnlyRule" :
331 p.second == UsdStageLoadRules::NoneRule ? "NoneRule" :
332 "<invalid value>") << ")";
333 }
334
335 std::ostream &
operator <<(std::ostream & os,UsdStageLoadRules const & rules)336 operator<<(std::ostream &os, UsdStageLoadRules const &rules)
337 {
338 return os << "UsdStageLoadRules(" << rules._rules << ")";
339 }
340
341 size_t
hash_value(UsdStageLoadRules const & rules)342 hash_value(UsdStageLoadRules const &rules)
343 {
344 boost::hash<std::vector<std::pair<SdfPath, UsdStageLoadRules::Rule> > > h;
345 return h(rules._rules);
346 }
347
348 PXR_NAMESPACE_CLOSE_SCOPE
349
350