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/hdSt/instancer.h"
25
26 #include "pxr/imaging/hdSt/drawItem.h"
27 #include "pxr/imaging/hdSt/primUtils.h"
28 #include "pxr/imaging/hdSt/resourceRegistry.h"
29 #include "pxr/imaging/hd/debugCodes.h"
30 #include "pxr/imaging/hd/rprimSharedData.h"
31 #include "pxr/imaging/hd/sceneDelegate.h"
32 #include "pxr/imaging/hd/vtBufferSource.h"
33
34 PXR_NAMESPACE_OPEN_SCOPE
35
36
HdStInstancer(HdSceneDelegate * delegate,SdfPath const & id)37 HdStInstancer::HdStInstancer(HdSceneDelegate* delegate,
38 SdfPath const &id)
39 : HdInstancer(delegate, id)
40 , _instancePrimvarNumElements(0)
41 {
42 }
43
44 void
Sync(HdSceneDelegate * sceneDelegate,HdRenderParam * renderParam,HdDirtyBits * dirtyBits)45 HdStInstancer::Sync(HdSceneDelegate *sceneDelegate,
46 HdRenderParam *renderParam,
47 HdDirtyBits *dirtyBits)
48 {
49 HD_TRACE_FUNCTION();
50 HF_MALLOC_TAG_FUNCTION();
51
52 SdfPath const& instancerId = GetId();
53
54 _UpdateInstancer(sceneDelegate, dirtyBits);
55 if (HdChangeTracker::IsAnyPrimvarDirty(*dirtyBits, instancerId)) {
56 _SyncPrimvars(sceneDelegate, dirtyBits);
57 }
58 }
59
60 void
_SyncPrimvars(HdSceneDelegate * sceneDelegate,HdDirtyBits * dirtyBits)61 HdStInstancer::_SyncPrimvars(HdSceneDelegate *sceneDelegate,
62 HdDirtyBits *dirtyBits)
63 {
64 SdfPath const& instancerId = GetId();
65
66 HdPrimvarDescriptorVector primvars =
67 HdStGetInstancerPrimvarDescriptors(this, sceneDelegate);
68
69 HdBufferSourceSharedPtrVector sources;
70 sources.reserve(primvars.size());
71
72 // Reset _instancePrimvarNumElements, in case the number of instances
73 // is varying.
74 _instancePrimvarNumElements= 0;
75
76 for (HdPrimvarDescriptor const& primvar: primvars) {
77 VtValue value = sceneDelegate->Get(instancerId, primvar.name);
78 if (!value.IsEmpty()) {
79 HdBufferSourceSharedPtr source;
80 if (primvar.name == HdInstancerTokens->instanceTransform &&
81 TF_VERIFY(value.IsHolding<VtArray<GfMatrix4d> >())) {
82 // Explicitly invoke the c'tor taking a
83 // VtArray<GfMatrix4d> to ensure we properly convert to
84 // the appropriate floating-point matrix type.
85 source.reset(new HdVtBufferSource(primvar.name,
86 value.UncheckedGet<VtArray<GfMatrix4d> >()));
87 }
88 else {
89 source.reset(new HdVtBufferSource(primvar.name, value));
90 }
91
92 // This is a defensive check, but ideally we would not be sent
93 // empty arrays from the client. Once UsdImaging can fulfill
94 // this contract efficiently, this check should emit a coding
95 // error.
96 if (source->GetNumElements() == 0) {
97 continue;
98 }
99
100 // Latch onto the first numElements we see.
101 size_t numElements = source->GetNumElements();
102 if (_instancePrimvarNumElements== 0) {
103 _instancePrimvarNumElements = numElements;
104 }
105
106 if (numElements != _instancePrimvarNumElements) {
107 // This primvar buffer is in a bad state; we can't have
108 // different numbers of instances per primvar. Trim to the
109 // lower value. Note: later on, we also trim the instance
110 // indices to be in this smaller range.
111 //
112 // This is recovery code; the scene delegate shouldn't let
113 // us get here...
114 TF_WARN("Inconsistent number of '%s' values "
115 "(%zu vs %zu) for <%s>.",
116 primvar.name.GetText(),
117 source->GetNumElements(),
118 _instancePrimvarNumElements,
119 instancerId.GetText());
120 _instancePrimvarNumElements
121 = std::min(numElements, _instancePrimvarNumElements);
122 }
123
124 sources.push_back(source);
125 }
126 }
127
128 if (!HdStCanSkipBARAllocationOrUpdate(
129 sources, _instancePrimvarRange, *dirtyBits)) {
130 // XXX: This should be based off the DirtyPrimvarDesc bit.
131 bool hasDirtyPrimvarDesc = (*dirtyBits & HdChangeTracker::DirtyPrimvar);
132 HdBufferSpecVector removedSpecs;
133 if (hasDirtyPrimvarDesc) {
134 TfTokenVector internallyGeneratedPrimvars; // none
135 removedSpecs = HdStGetRemovedPrimvarBufferSpecs(
136 _instancePrimvarRange, primvars,
137 internallyGeneratedPrimvars, instancerId);
138 }
139
140 HdBufferSpecVector bufferSpecs;
141 HdBufferSpec::GetBufferSpecs(sources, &bufferSpecs);
142
143 HdStResourceRegistrySharedPtr const& resourceRegistry =
144 std::static_pointer_cast<HdStResourceRegistry>(
145 sceneDelegate->GetRenderIndex().GetResourceRegistry());
146
147 // Update local primvar range.
148 _instancePrimvarRange =
149 resourceRegistry->UpdateNonUniformBufferArrayRange(
150 HdTokens->primvar, _instancePrimvarRange, bufferSpecs,
151 removedSpecs, HdBufferArrayUsageHint());
152
153 TF_VERIFY(_instancePrimvarRange->IsValid());
154
155 // schedule to sync gpu
156 if (!sources.empty()) {
157 resourceRegistry->AddSources(
158 _instancePrimvarRange, std::move(sources));
159 }
160 }
161 }
162
163 void
_GetInstanceIndices(SdfPath const & prototypeId,std::vector<VtIntArray> * instanceIndicesArray)164 HdStInstancer::_GetInstanceIndices(SdfPath const &prototypeId,
165 std::vector<VtIntArray> *instanceIndicesArray)
166 {
167 SdfPath const &instancerId = GetId();
168 VtIntArray instanceIndices
169 = GetDelegate()->GetInstanceIndices(instancerId, prototypeId);
170
171 // quick sanity check
172 // instance indices should not exceed the size of instance primvars.
173 for (auto it = instanceIndices.cbegin();
174 it != instanceIndices.cend(); ++it) {
175 if (*it >= (int)_instancePrimvarNumElements) {
176 TF_WARN("Instance index exceeds the element count of instance "
177 "primvars (%d >= %zu) for <%s>",
178 *it, _instancePrimvarNumElements, instancerId.GetText());
179 instanceIndices.clear();
180 // insert 0-th index as placeholder (0th should always exist, since
181 // we don't populate instance primvars with numElements == 0).
182 instanceIndices.push_back(0);
183 break;
184 }
185 }
186
187 instanceIndicesArray->push_back(instanceIndices);
188
189 if (TfDebug::IsEnabled(HD_INSTANCER_UPDATED)) {
190 std::stringstream ss;
191 ss << instanceIndices;
192 TF_DEBUG(HD_INSTANCER_UPDATED).Msg("GetInstanceIndices for proto <%s> "
193 "instancer <%s> (parent: <%s>): %s\n",
194 prototypeId.GetText(),
195 instancerId.GetText(),
196 GetParentId().GetText(),
197 ss.str().c_str());
198 }
199
200 // backtrace the instancer hierarchy to gather all instance indices.
201 if (!GetParentId().IsEmpty()) {
202 HdInstancer *parentInstancer =
203 GetDelegate()->GetRenderIndex().GetInstancer(GetParentId());
204 if (TF_VERIFY(parentInstancer)) {
205 static_cast<HdStInstancer*>(parentInstancer)->
206 _GetInstanceIndices(instancerId, instanceIndicesArray);
207 }
208 }
209 }
210
211 VtIntArray
GetInstanceIndices(SdfPath const & prototypeId)212 HdStInstancer::GetInstanceIndices(SdfPath const &prototypeId)
213 {
214 HD_TRACE_FUNCTION();
215 HF_MALLOC_TAG_FUNCTION();
216
217 // delegate provides sparse index array for prototypeId.
218 std::vector<VtIntArray> instanceIndicesArray;
219 _GetInstanceIndices(prototypeId, &instanceIndicesArray);
220 int instancerNumLevels = (int)instanceIndicesArray.size();
221
222 if (!TF_VERIFY(instancerNumLevels > 0)) {
223 return VtIntArray();
224 }
225
226 // create the cartesian product of instanceIndices array. Each tuple is
227 // preceded by a global instance index <n>.
228 // e.g.
229 // input : [0,1] [3,4,5] [7,8]
230 // output : [<0>,0,3,7, <1>,1,3,7, <2>,0,4,7, <3>,1,4,7,
231 // <4>,0,5,7, <5>,1,5,7, <6>,0,3,8, ...]
232
233 size_t nTotal = 1;
234 for (int i = 0; i < instancerNumLevels; ++i) {
235 nTotal *= instanceIndicesArray[i].size();
236 }
237 int instanceIndexWidth = 1 + instancerNumLevels;
238
239 VtIntArray instanceIndices(nTotal * instanceIndexWidth);
240 std::vector<int> currents(instancerNumLevels);
241 for (size_t j = 0; j < nTotal; ++j) {
242 instanceIndices[j*instanceIndexWidth] = j; // global idx
243 for (int i = 0; i < instancerNumLevels; ++i) {
244 instanceIndices[j*instanceIndexWidth + i + 1] =
245 instanceIndicesArray[i].cdata()[currents[i]];
246 }
247 ++currents[0];
248 for (int i = 0; i < instancerNumLevels-1; ++i) {
249 if (static_cast<size_t>(currents[i]) >=
250 instanceIndicesArray[i].size()) {
251 ++currents[i+1];
252 currents[i] = 0;
253 }
254 }
255 }
256
257 if (TfDebug::IsEnabled(HD_INSTANCER_UPDATED)) {
258 std::stringstream ss;
259 ss << instanceIndices;
260 TF_DEBUG(HD_INSTANCER_UPDATED).Msg("Flattened indices <%s>: %s\n",
261 prototypeId.GetText(),
262 ss.str().c_str());
263 }
264
265 return instanceIndices;
266 }
267
268 PXR_NAMESPACE_CLOSE_SCOPE
269
270