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/pxr.h"
25 #include "pxr/usd/usd/prim.h"
26
27 #include "pxr/usd/usd/debugCodes.h"
28 #include "pxr/usd/usd/errors.h"
29 #include "pxr/usd/usd/instanceCache.h"
30 #include "pxr/usd/usd/resolver.h"
31 #include "pxr/usd/usd/stage.h"
32 #include "pxr/usd/usd/tokens.h"
33 #include "pxr/usd/usd/primRange.h"
34
35 #include "pxr/usd/kind/registry.h"
36
37 #include "pxr/base/tf/exception.h"
38 #include "pxr/base/tf/stringUtils.h"
39
40 #include <algorithm>
41 #include <sstream>
42 #include <vector>
43
44 PXR_NAMESPACE_OPEN_SCOPE
45
46 // Static assertion on PrimData size. We want to be warned when its size
47 // changes.
48 static_assert(sizeof(Usd_PrimData) == 64,
49 "Expected sizeof(Usd_PrimData) == 64");
50
51 // Usd_PrimData need to be always initialized with a valid type info pointer
_GetEmptyPrimTypeInfo()52 static const UsdPrimTypeInfo *_GetEmptyPrimTypeInfo()
53 {
54 static const UsdPrimTypeInfo *empty = &UsdPrimTypeInfo::GetEmptyPrimType();
55 return empty;
56 }
57
Usd_PrimData(UsdStage * stage,const SdfPath & path)58 Usd_PrimData::Usd_PrimData(UsdStage *stage, const SdfPath& path)
59 : _stage(stage)
60 , _primIndex(nullptr)
61 , _path(path)
62 , _primTypeInfo(_GetEmptyPrimTypeInfo())
63 , _firstChild(nullptr)
64 , _refCount(0)
65 {
66 if (!stage)
67 TF_FATAL_ERROR("Attempted to construct with null stage");
68
69 TF_DEBUG(USD_PRIM_LIFETIMES).Msg(
70 "Usd_PrimData::ctor<%s,%s,%s>\n",
71 GetTypeName().GetText(), path.GetText(),
72 _stage->GetRootLayer()->GetIdentifier().c_str());
73 }
74
~Usd_PrimData()75 Usd_PrimData::~Usd_PrimData() {
76 TF_DEBUG(USD_PRIM_LIFETIMES).Msg(
77 "~Usd_PrimData::dtor<%s,%s,%s>\n",
78 GetTypeName().GetText(), _path.GetText(),
79 _stage ? _stage->GetRootLayer()->GetIdentifier().c_str() :
80 "prim is invalid/expired");
81 }
82
83 Usd_PrimDataConstPtr
GetParent() const84 Usd_PrimData::GetParent() const
85 {
86 if (Usd_PrimDataPtr parentLink = GetParentLink())
87 return parentLink;
88
89 SdfPath parent = _path.GetParentPath();
90 return parent == SdfPath::EmptyPath() ?
91 nullptr : _stage->_GetPrimDataAtPath(parent);
92 }
93
94 const PcpPrimIndex &
GetPrimIndex() const95 Usd_PrimData::GetPrimIndex() const
96 {
97 static const PcpPrimIndex dummyPrimIndex;
98 return ARCH_UNLIKELY(IsPrototype()) ? dummyPrimIndex : *_primIndex;
99 }
100
101 const PcpPrimIndex &
GetSourcePrimIndex() const102 Usd_PrimData::GetSourcePrimIndex() const
103 {
104 TF_AXIOM(_primIndex);
105 return *_primIndex;
106 }
107
108 SdfSpecifier
GetSpecifier() const109 Usd_PrimData::GetSpecifier() const
110 {
111 return UsdStage::_GetSpecifier(this);
112 }
113
114 void
_ComposeAndCacheFlags(Usd_PrimDataConstPtr parent,bool isPrototypePrim)115 Usd_PrimData::_ComposeAndCacheFlags(Usd_PrimDataConstPtr parent,
116 bool isPrototypePrim)
117 {
118 // We do not have to clear _flags here since in the pseudo root or instance
119 // prototype case the values never change, and in the ordinary prim case we
120 // set every flag (with the exception of the pseudo root flag which is only
121 // set true for the pseudo root and always remains false for every other
122 // prim)
123
124 // Special-case the root (the only prim which has no parent) and
125 // instancing prototypes.
126 if (ARCH_UNLIKELY(!parent || isPrototypePrim)) {
127 _flags[Usd_PrimActiveFlag] = true;
128 _flags[Usd_PrimLoadedFlag] = true;
129 _flags[Usd_PrimModelFlag] = true;
130 _flags[Usd_PrimGroupFlag] = true;
131 _flags[Usd_PrimDefinedFlag] = true;
132 _flags[Usd_PrimHasDefiningSpecifierFlag] = true;
133 _flags[Usd_PrimPrototypeFlag] = isPrototypePrim;
134 _flags[Usd_PrimPseudoRootFlag] = !parent;
135 }
136 else {
137 // Compose and cache 'active'.
138 const bool active = UsdStage::_IsActive(this);
139 _flags[Usd_PrimActiveFlag] = active;
140
141 // Cache whether or not this prim has a payload.
142 bool hasPayload = _primIndex->HasAnyPayloads();
143 _flags[Usd_PrimHasPayloadFlag] = hasPayload;
144
145 // An active prim is loaded if it's loadable and in the load set, or
146 // it's not loadable and its parent is loaded.
147 _flags[Usd_PrimLoadedFlag] = active &&
148 (hasPayload ?
149 _stage->_GetPcpCache()->IsPayloadIncluded(_primIndex->GetPath()) :
150 parent->IsLoaded());
151
152 // According to Model hierarchy rules, only Model Groups may have Model
153 // children (groups or otherwise). So if our parent is not a Model
154 // Group, then this prim cannot be a model (or a model group).
155 // Otherwise we look up the kind metadata and consult the kind registry.
156 bool isGroup = false, isModel = false;
157 if (parent->IsGroup()) {
158 const TfToken kind = UsdStage::_GetKind(this);
159 // Use the kind registry to determine model/groupness.
160 if (!kind.IsEmpty()) {
161 isGroup = KindRegistry::IsA(kind, KindTokens->group);
162 isModel = isGroup || KindRegistry::IsA(kind, KindTokens->model);
163 }
164 }
165 _flags[Usd_PrimGroupFlag] = isGroup;
166 _flags[Usd_PrimModelFlag] = isModel;
167
168 // Get specifier.
169 const SdfSpecifier specifier = GetSpecifier();
170
171 // This prim is abstract if its parent is or if it's a class.
172 _flags[Usd_PrimAbstractFlag] =
173 parent->IsAbstract() || specifier == SdfSpecifierClass;
174
175 // Cache whether or not this prim has an authored defining specifier.
176 const bool isDefiningSpec = SdfIsDefiningSpecifier(specifier);
177 _flags[Usd_PrimHasDefiningSpecifierFlag] = isDefiningSpec;
178
179 // This prim is defined if its parent is and its specifier is defining.
180 _flags[Usd_PrimDefinedFlag] = isDefiningSpec && parent->IsDefined();
181
182 // The presence of clips that may affect attributes on this prim
183 // is computed and set in UsdStage. Default to false.
184 _flags[Usd_PrimClipsFlag] = false;
185
186 // These flags indicate whether this prim is an instance or an
187 // instance prototype.
188 _flags[Usd_PrimInstanceFlag] = active && _primIndex->IsInstanceable();
189 _flags[Usd_PrimPrototypeFlag] = parent->IsInPrototype();
190 }
191 }
192
193 Usd_PrimDataConstPtr
GetPrimDataAtPathOrInPrototype(const SdfPath & path) const194 Usd_PrimData::GetPrimDataAtPathOrInPrototype(const SdfPath &path) const
195 {
196 return _stage->_GetPrimDataAtPathOrInPrototype(path);
197 }
198
199 Usd_PrimDataConstPtr
GetPrototype() const200 Usd_PrimData::GetPrototype() const
201 {
202 return _stage->_GetPrototypeForInstance(this);
203 }
204
205 bool
_ComposePrimChildNames(TfTokenVector * nameOrder)206 Usd_PrimData::_ComposePrimChildNames(TfTokenVector* nameOrder)
207 {
208 // TODO: would be nice to not compute the name order until it is needed
209 // TODO: What do we do with prohibitedNames?
210 PcpTokenSet prohibitedNames;
211 GetSourcePrimIndex().ComputePrimChildNames(nameOrder, &prohibitedNames);
212 return true;
213 }
214
215 std::string
Usd_DescribePrimData(const Usd_PrimData * p,SdfPath const & proxyPrimPath)216 Usd_DescribePrimData(const Usd_PrimData *p, SdfPath const &proxyPrimPath)
217 {
218 if (!p)
219 return "null prim";
220
221 bool isInstance = p->IsInstance();
222 bool isInstanceProxy = Usd_IsInstanceProxy(p, proxyPrimPath);
223 bool isInPrototype = isInstanceProxy ?
224 Usd_InstanceCache::IsPathInPrototype(proxyPrimPath) :
225 p->IsInPrototype();
226 bool isPrototype = p->IsPrototype();
227 Usd_PrimDataConstPtr prototypeForInstance =
228 isInstance && p->_stage ? p->GetPrototype() : nullptr;
229
230 return TfStringPrintf(
231 "%s%s%sprim %s<%s> %s%s%s",
232 Usd_IsDead(p) ? "expired " : (p->_flags[Usd_PrimActiveFlag] ?
233 "" : "inactive "),
234 p->GetTypeName().IsEmpty() ? "" :
235 TfStringPrintf("'%s' ", p->GetTypeName().GetText()).c_str(),
236 // XXX: Add applied schemas to this descriptor
237 isInstance ? "instance " : isInstanceProxy ? "instance proxy " : "",
238 isInPrototype ? "in prototype " : "",
239 isInstanceProxy ? proxyPrimPath.GetText() : p->_path.GetText(),
240 (isInstanceProxy || isInstance) ? TfStringPrintf(
241 "with prototype <%s> ", isInstance ?
242 prototypeForInstance->GetPath().GetText() :
243 p->_path.GetText()).c_str() : "",
244 (isInstanceProxy || isPrototype || isInPrototype) ? TfStringPrintf(
245 "using prim index <%s> ",
246 p->GetSourcePrimIndex().GetPath().GetText()).c_str() : "",
247 p->_stage ? TfStringPrintf(
248 "on %s", UsdDescribe(p->_stage).c_str()).c_str() : ""
249 );
250 }
251
252 void
Usd_ThrowExpiredPrimAccessError(const Usd_PrimData * p)253 Usd_ThrowExpiredPrimAccessError(const Usd_PrimData *p)
254 {
255 TF_THROW(UsdExpiredPrimAccessError,
256 TfStringPrintf(
257 "Used %s", Usd_DescribePrimData(p, SdfPath()).c_str()));
258 }
259
260
261 PXR_NAMESPACE_CLOSE_SCOPE
262
263