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