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/imaging/hdSt/volume.h"
25
26 #include "pxr/imaging/hdSt/drawItem.h"
27 #include "pxr/imaging/hdSt/field.h"
28 #include "pxr/imaging/hdSt/material.h"
29 #include "pxr/imaging/hdSt/materialNetworkShader.h"
30 #include "pxr/imaging/hdSt/materialParam.h"
31 #include "pxr/imaging/hdSt/package.h"
32 #include "pxr/imaging/hdSt/primUtils.h"
33 #include "pxr/imaging/hdSt/renderParam.h"
34 #include "pxr/imaging/hdSt/resourceBinder.h"
35 #include "pxr/imaging/hdSt/resourceRegistry.h"
36 #include "pxr/imaging/hdSt/textureBinder.h"
37 #include "pxr/imaging/hdSt/tokens.h"
38 #include "pxr/imaging/hdSt/volumeShader.h"
39 #include "pxr/imaging/hdSt/volumeShaderKey.h"
40
41 #include "pxr/imaging/hd/sceneDelegate.h"
42 #include "pxr/imaging/hd/vtBufferSource.h"
43
44 #include "pxr/imaging/hf/diagnostic.h"
45
46 #include "pxr/imaging/hio/glslfx.h"
47
48 #include "pxr/base/tf/staticTokens.h"
49
50 PXR_NAMESPACE_OPEN_SCOPE
51
52 TF_DEFINE_PRIVATE_TOKENS(
53 _fallbackShaderTokens,
54
55 (density)
56 (emission)
57 );
58
59 const float HdStVolume::defaultStepSize = 1.0f;
60 const float HdStVolume::defaultStepSizeLighting = 10.0f;
61 const float HdStVolume::defaultMaxTextureMemoryPerField = 128.0f;
62
HdStVolume(SdfPath const & id)63 HdStVolume::HdStVolume(SdfPath const& id)
64 : HdVolume(id)
65 {
66 }
67
68 HdStVolume::~HdStVolume() = default;
69
70 // Dirty bits requiring recomputing the material shader and the
71 // bounding box.
72 static const int _shaderAndBBoxComputationDirtyBitsMask =
73 HdChangeTracker::Clean
74 | HdChangeTracker::DirtyExtent
75 | HdChangeTracker::DirtyMaterialId
76 | HdChangeTracker::DirtyRepr
77 | HdChangeTracker::DirtyVolumeField;
78
79 static const int _initialDirtyBitsMask =
80 _shaderAndBBoxComputationDirtyBitsMask
81 | HdChangeTracker::DirtyPrimID
82 | HdChangeTracker::DirtyPrimvar
83 | HdChangeTracker::DirtyTransform
84 | HdChangeTracker::DirtyVisibility
85 | HdChangeTracker::DirtyInstancer;
86
87 HdDirtyBits
GetInitialDirtyBitsMask() const88 HdStVolume::GetInitialDirtyBitsMask() const
89 {
90 int mask = _initialDirtyBitsMask;
91 return (HdDirtyBits)mask;
92 }
93
94 HdDirtyBits
_PropagateDirtyBits(HdDirtyBits bits) const95 HdStVolume::_PropagateDirtyBits(HdDirtyBits bits) const
96 {
97 return bits;
98 }
99
100 void
_InitRepr(TfToken const & reprToken,HdDirtyBits * dirtyBits)101 HdStVolume::_InitRepr(TfToken const &reprToken, HdDirtyBits* dirtyBits)
102 {
103 // All representations point to _volumeRepr.
104 if (!_volumeRepr) {
105 _volumeRepr = std::make_shared<HdRepr>();
106 _volumeRepr->AddDrawItem(
107 std::make_unique<HdStDrawItem>(&_sharedData));
108 *dirtyBits |= HdChangeTracker::NewRepr;
109 }
110
111 _ReprVector::iterator it = std::find_if(_reprs.begin(), _reprs.end(),
112 _ReprComparator(reprToken));
113 bool isNew = it == _reprs.end();
114 if (isNew) {
115 // add new repr
116 it = _reprs.insert(_reprs.end(),
117 std::make_pair(reprToken, _volumeRepr));
118 }
119 }
120
121 void
Sync(HdSceneDelegate * delegate,HdRenderParam * renderParam,HdDirtyBits * dirtyBits,TfToken const & reprToken)122 HdStVolume::Sync(HdSceneDelegate *delegate,
123 HdRenderParam *renderParam,
124 HdDirtyBits *dirtyBits,
125 TfToken const &reprToken)
126 {
127 _UpdateVisibility(delegate, dirtyBits);
128
129 if (*dirtyBits & HdChangeTracker::DirtyMaterialId) {
130 HdStSetMaterialId(delegate, renderParam, this);
131
132 HdStDrawItem * const drawItem = static_cast<HdStDrawItem*>(
133 _volumeRepr->GetDrawItem(0));
134 HdStSetMaterialTag(renderParam, drawItem,
135 HdStMaterialTagTokens->volume);
136 }
137
138 _UpdateRepr(delegate, renderParam, reprToken, dirtyBits);
139
140 // This clears all the non-custom dirty bits. This ensures that the rprim
141 // doesn't have pending dirty bits that add it to the dirty list every
142 // frame.
143 // XXX: GetInitialDirtyBitsMask sets certain dirty bits that aren't
144 // reset (e.g. DirtyExtent, DirtyPrimID) that make this necessary.
145 *dirtyBits &= ~HdChangeTracker::AllSceneDirtyBits;
146 }
147
148 void
Finalize(HdRenderParam * renderParam)149 HdStVolume::Finalize(HdRenderParam *renderParam)
150 {
151 HdStMarkGarbageCollectionNeeded(renderParam);
152
153 HdStRenderParam * const stRenderParam =
154 static_cast<HdStRenderParam*>(renderParam);
155
156 // Decrement material tag count for volume material tag
157 stRenderParam->DecreaseMaterialTagCount(HdStMaterialTagTokens->volume);
158 }
159
160 void
_UpdateRepr(HdSceneDelegate * sceneDelegate,HdRenderParam * renderParam,TfToken const & reprToken,HdDirtyBits * dirtyBits)161 HdStVolume::_UpdateRepr(HdSceneDelegate *sceneDelegate,
162 HdRenderParam *renderParam,
163 TfToken const &reprToken,
164 HdDirtyBits *dirtyBits)
165 {
166 HD_TRACE_FUNCTION();
167 HF_MALLOC_TAG_FUNCTION();
168
169 HdReprSharedPtr const &curRepr = _volumeRepr;
170
171 if (TfDebug::IsEnabled(HD_RPRIM_UPDATED)) {
172 HdChangeTracker::DumpDirtyBits(*dirtyBits);
173 }
174
175 HdStDrawItem * const drawItem = static_cast<HdStDrawItem*>(
176 curRepr->GetDrawItem(0));
177
178 if (HdChangeTracker::IsDirty(*dirtyBits)) {
179 _UpdateDrawItem(sceneDelegate, renderParam, drawItem, dirtyBits);
180 }
181
182 *dirtyBits &= ~HdChangeTracker::NewRepr;
183 }
184
185 namespace {
186
187 // Fallback volume data created from shaders/fallbackVolume.glslfx
188 HdStMaterial::VolumeMaterialData
_MakeFallbackVolumeMaterialData()189 _MakeFallbackVolumeMaterialData()
190 {
191 const HioGlslfx glslfx(HdStPackageFallbackVolumeShader());
192
193 return
194 {
195 glslfx.GetVolumeSource(),
196 {
197 HdSt_MaterialParam(
198 HdSt_MaterialParam::ParamTypeFieldRedirect,
199 _fallbackShaderTokens->density,
200 VtValue(0.0f),
201 { _fallbackShaderTokens->density }),
202 HdSt_MaterialParam(
203 HdSt_MaterialParam::ParamTypeFieldRedirect,
204 _fallbackShaderTokens->emission,
205 VtValue(GfVec3f(0.0, 0.0, 0.0)),
206 { _fallbackShaderTokens->emission })
207 }
208 };
209 }
210
211 const
212 HdStMaterial::VolumeMaterialData &
_ComputeVolumeMaterialData(const HdStMaterial * const material)213 _ComputeVolumeMaterialData(const HdStMaterial * const material)
214 {
215 // Try to use volume material data from material.
216 if (material) {
217 const HdStMaterial::VolumeMaterialData &data =
218 material->GetVolumeMaterialData();
219 if (!data.source.empty()) {
220 return data;
221 }
222 }
223
224 // Instantiate fallback volume shader only once
225 //
226 // Note that the default HdStMaterial provides a fallback surface
227 // shader and we need a volume shader, so we create the shader here
228 // ourselves.
229 static const HdStMaterial::VolumeMaterialData fallbackData =
230 _MakeFallbackVolumeMaterialData();
231 return fallbackData;
232 }
233
234 // A map from name to HdStVolumeFieldDescriptor (identifying a
235 // field prim).
236 //
237 // Initialized from a volume prim identified by its path. In the usd world,
238 // this map is created by following the field:NAME relationships on the volume
239 // prim to the targeted field prims. The information identifiying the field
240 // prim is inserted under the key NAME.
241 //
242 class _NameToFieldDescriptor
243 {
244 public:
245 // Get information from scene delegate and create map.
246 //
247 // Issue validation error if relationship did not target a field prim.
248 //
_NameToFieldDescriptor(HdSceneDelegate * const sceneDelegate,const SdfPath & id)249 _NameToFieldDescriptor(
250 HdSceneDelegate * const sceneDelegate,
251 const SdfPath &id)
252 : _descriptors(sceneDelegate->GetVolumeFieldDescriptors(id))
253 {
254 for (const HdVolumeFieldDescriptor &desc : _descriptors) {
255 if (dynamic_cast<HdStField*>(
256 sceneDelegate->GetRenderIndex().GetBprim(
257 desc.fieldPrimType, desc.fieldId))) {
258
259 _nameToDescriptor.insert({desc.fieldName, &desc});
260
261 } else {
262 HF_VALIDATION_WARN(
263 id,
264 "Volume has field relationship to non-field prim %s.",
265 desc.fieldId.GetText());
266 }
267 }
268 }
269
270 // Get information identifiying field prim associated to given name.
271 //
272 // Returns nullptr if no such field prim. Lifetime of returned object
273 // is tied to _NameToFieldDescriptor.
274 //
275 const HdVolumeFieldDescriptor *
GetDescriptor(const TfToken & name) const276 GetDescriptor(const TfToken &name) const {
277 const auto it = _nameToDescriptor.find(name);
278 if (it == _nameToDescriptor.end()) {
279 return nullptr;
280 }
281 return it->second;
282 }
283
284 private:
285 using _NameToDescriptor =
286 std::unordered_map<TfToken,
287 const HdVolumeFieldDescriptor *,
288 TfToken::HashFunctor>;
289 HdVolumeFieldDescriptorVector _descriptors;
290 _NameToDescriptor _nameToDescriptor;
291 };
292
293 // Add GLSL code such as "HdGet_density(vec3 p)" for sampling the fields
294 // to the volume shader code and add necessary 3d textures and other
295 // parameters and buffer sources to the resulting HdSt_VolumeShader.
296 // HdMaterialParam's are consulted to figure out the names of the fields
297 // to sample and the names of the associated sampling functions to generate.
298 //
299 // The resulting shader can also fill the points bar of the volume computed
300 // from the bounding box of the volume.
301 //
302 HdSt_VolumeShaderSharedPtr
_ComputeMaterialNetworkShader(HdSceneDelegate * const sceneDelegate,const SdfPath & id,const HdStMaterial::VolumeMaterialData & volumeMaterialData,const GfRange3d & authoredExtents)303 _ComputeMaterialNetworkShader(
304 HdSceneDelegate * const sceneDelegate,
305 const SdfPath &id,
306 const HdStMaterial::VolumeMaterialData &volumeMaterialData,
307 const GfRange3d &authoredExtents)
308 {
309 TRACE_FUNCTION();
310
311 HdStResourceRegistrySharedPtr const resourceRegistry =
312 std::static_pointer_cast<HdStResourceRegistry>(
313 sceneDelegate->GetRenderIndex().GetResourceRegistry());
314
315 // Generate new shader from volume shader
316 HdSt_VolumeShaderSharedPtr const result =
317 std::make_shared<HdSt_VolumeShader>(
318 sceneDelegate->GetRenderIndex().GetRenderDelegate());
319
320 // Buffer specs and source for the shader BAR
321 HdBufferSpecVector bufferSpecs;
322 HdBufferSourceSharedPtrVector bufferSources;
323
324 // The names of the fields read by field readers.
325 std::set<TfToken> fieldNames;
326
327 for (const auto & param : volumeMaterialData.params) {
328 // Scan original parameters...
329 if ( param.IsFieldRedirect() ||
330 param.IsPrimvarRedirect() ||
331 param.IsFallback() ) {
332 // Add fallback values for parameters
333 HdSt_MaterialNetworkShader::AddFallbackValueToSpecsAndSources(
334 param, &bufferSpecs, &bufferSources);
335
336 if (param.IsFieldRedirect()) {
337 // Determine the name of the field the field reader requests.
338 TfTokenVector const &names = param.samplerCoords;
339 if (!names.empty()) {
340 fieldNames.insert(names[0]);
341 }
342 }
343 }
344 // Ignoring 2D texture parameters for volumes.
345 }
346
347 // Make a copy of the original params
348 HdSt_MaterialParamVector params = volumeMaterialData.params;
349
350 // Note that it is a requirement of HdSt_VolumeShader that
351 // namedTextureHandles and fieldDescs line up.
352 HdStShaderCode::NamedTextureHandleVector namedTextureHandles;
353 HdVolumeFieldDescriptorVector fieldDescs;
354
355 const _NameToFieldDescriptor _nameToFieldDescriptor(sceneDelegate, id);
356
357 // For each requested field name, record the information needed to
358 // allocate the necessary texture later:
359 // - a texture HdSt_MaterialParam
360 // - an HdVolumeFieldDescriptor identifying the HdStField prim holding
361 // the path to the texture
362 // - a HdStShader::NamedTextureHandle initialized with a null-handle.
363 //
364 for (const auto & fieldName : fieldNames) {
365 // See whether we have the the field in the volume field
366 // descriptors given to us by the scene delegate.
367 const HdVolumeFieldDescriptor * const desc =
368 _nameToFieldDescriptor.GetDescriptor(fieldName);
369 if (!desc) {
370 // Invalid field prim, skip.
371 continue;
372 }
373
374 // Record field descriptor
375 fieldDescs.push_back(*desc);
376
377 const TfToken textureName(
378 fieldName.GetString() +
379 HdSt_ResourceBindingSuffixTokens->texture.GetString());
380 static const HdTextureType textureType = HdTextureType::Field;
381
382 // Produce HdGet_FIELDNAME_texture(vec3 p) to sample
383 // the texture.
384 const HdSt_MaterialParam param(
385 HdSt_MaterialParam::ParamTypeTexture,
386 textureName,
387 VtValue(GfVec4f(0)),
388 TfTokenVector(),
389 textureType);
390
391 HdSt_MaterialNetworkShader::AddFallbackValueToSpecsAndSources(
392 param, &bufferSpecs, &bufferSources);
393
394 params.push_back(param);
395
396 namedTextureHandles.push_back(
397 { textureName, textureType, nullptr, desc->fieldId.GetHash() });
398 }
399
400 // Get buffer specs for textures (i.e., for
401 // field sampling transforms and bindless texture handles).
402 HdSt_TextureBinder::GetBufferSpecs(namedTextureHandles, &bufferSpecs);
403
404 // Create params (so that HdGet_... are created) and buffer specs,
405 // to communicate volume bounding box and sample distance to shader.
406 HdSt_VolumeShader::GetParamsAndBufferSpecsForBBoxAndSampleDistance(
407 ¶ms, &bufferSpecs);
408
409 const bool hasField = !namedTextureHandles.empty();
410
411 // If there is a field, we postpone giving buffer sources for
412 // the volume bounding box until after the textures have been
413 // committed.
414 if (!hasField) {
415 HdSt_VolumeShader::GetBufferSourcesForBBoxAndSampleDistance(
416 { GfBBox3d(authoredExtents), 1.0f },
417 &bufferSources);
418 }
419
420 // Make volume shader responsible if we have fields with bounding
421 // boxes.
422 result->SetFillsPointsBar(hasField);
423 result->SetParams(params);
424 result->SetBufferSources(
425 bufferSpecs, std::move(bufferSources), resourceRegistry);
426 result->SetNamedTextureHandles(namedTextureHandles);
427 result->SetFieldDescriptors(fieldDescs);
428
429 // Append the volume shader (calling into the GLSL functions
430 // generated above)
431 result->SetFragmentSource(volumeMaterialData.source);
432
433 return result;
434 }
435
436 VtValue
_ComputeBBoxVertices(GfRange3d const & range)437 _ComputeBBoxVertices(GfRange3d const &range)
438 {
439 VtVec3fArray result(8);
440
441 const GfVec3d min = HdSt_VolumeShader::GetSafeMin(range);
442 const GfVec3d max = HdSt_VolumeShader::GetSafeMax(range);
443
444 int i = 0;
445
446 for (const double x : { min[0], max[0] }) {
447 for (const double y : { min[1], max[1] }) {
448 for (const double z : { min[2], max[2] }) {
449 result[i] = GfVec3f(x,y,z);
450 i++;
451 }
452 }
453 }
454
455 return VtValue(result);
456 }
457
458 const VtValue &
_GetCubeTriangleIndices()459 _GetCubeTriangleIndices()
460 {
461 static const VtValue result(
462 VtVec3iArray{
463 GfVec3i(1,3,2),
464 GfVec3i(0,1,2),
465
466 GfVec3i(7,5,4),
467 GfVec3i(6,7,4),
468
469 GfVec3i(5,1,0),
470 GfVec3i(4,5,0),
471
472 GfVec3i(3,7,6),
473 GfVec3i(2,3,6),
474
475 GfVec3i(2,6,4),
476 GfVec3i(0,2,4),
477
478 GfVec3i(7,3,1),
479 GfVec3i(5,7,1)});
480
481 return result;
482 }
483
484 } // end namespace
485
486 void
_UpdateDrawItem(HdSceneDelegate * sceneDelegate,HdRenderParam * renderParam,HdStDrawItem * drawItem,HdDirtyBits * dirtyBits)487 HdStVolume::_UpdateDrawItem(HdSceneDelegate *sceneDelegate,
488 HdRenderParam *renderParam,
489 HdStDrawItem *drawItem,
490 HdDirtyBits *dirtyBits)
491 {
492 HD_TRACE_FUNCTION();
493 HF_MALLOC_TAG_FUNCTION();
494
495 if (HdStShouldPopulateConstantPrimvars(dirtyBits, GetId())) {
496 /* CONSTANT PRIMVARS, TRANSFORM AND EXTENT */
497 const HdPrimvarDescriptorVector constantPrimvars =
498 HdStGetPrimvarDescriptors(this, drawItem, sceneDelegate,
499 HdInterpolationConstant);
500 HdStPopulateConstantPrimvars(this,
501 &_sharedData,
502 sceneDelegate,
503 renderParam,
504 drawItem,
505 dirtyBits,
506 constantPrimvars);
507 }
508
509 if ((*dirtyBits) & HdChangeTracker::DirtyMaterialId) {
510 /* MATERIAL SHADER (may affect subsequent primvar population) */
511
512 // Note that the creation of the HdSt_VolumeShader and the
513 // allocation of the necessary textures is driven by two
514 // different dirtyBits (HdChangeTracker::DirtyMaterialId and
515 // HdChangeTracker::DirtyVolumeField).
516 //
517 // This way, we do not need to re-create the shader on every frame
518 // when the fields of a volume are animated.
519 //
520 const HdStMaterial * const material = static_cast<const HdStMaterial *>(
521 sceneDelegate->GetRenderIndex().GetSprim(
522 HdPrimTypeTokens->material, GetMaterialId()));
523
524 // Compute the material shader by adding GLSL code such as
525 // "HdGet_density(vec3 p)" for sampling the fields needed by the volume
526 // shader.
527 // The material shader will eventually be concatenated with
528 // the geometry shader which does the raymarching and is calling into
529 // GLSL functions such as "float scattering(vec3)" in the volume shader
530 // to evaluate physical properties of a volume at the point p.
531
532 drawItem->SetMaterialNetworkShader(
533 _ComputeMaterialNetworkShader(
534 sceneDelegate,
535 GetId(),
536 _ComputeVolumeMaterialData(material),
537 _sharedData.bounds.GetRange()));
538 }
539
540 HdStResourceRegistrySharedPtr resourceRegistry =
541 std::static_pointer_cast<HdStResourceRegistry>(
542 sceneDelegate->GetRenderIndex().GetResourceRegistry());
543
544 HdSt_VolumeShaderSharedPtr const materialNetworkShader =
545 std::dynamic_pointer_cast<HdSt_VolumeShader>(
546 drawItem->GetMaterialNetworkShader());
547
548 if (!materialNetworkShader) {
549 TF_CODING_ERROR("Expected valid volume shader for draw item.");
550 return;
551 }
552
553 if ((*dirtyBits) & (HdChangeTracker::DirtyVolumeField |
554 HdChangeTracker::DirtyMaterialId)) {
555 /* FIELD TEXTURES */
556
557 // (Re-)Allocate the textures associated with the field prims.
558 materialNetworkShader->UpdateTextureHandles(sceneDelegate);
559 }
560
561 /* VERTICES */
562 if ((*dirtyBits) & _shaderAndBBoxComputationDirtyBitsMask) {
563 // Any change to the bounding box requires us to recompute
564 // the vertices
565 //
566 if (!HdStIsValidBAR(drawItem->GetVertexPrimvarRange())) {
567 static const HdBufferSpecVector bufferSpecs{
568 HdBufferSpec(HdTokens->points,
569 HdTupleType{ HdTypeFloatVec3, 1 })
570 };
571
572 HdBufferArrayRangeSharedPtr const range =
573 resourceRegistry->AllocateNonUniformBufferArrayRange(
574 HdTokens->primvar, bufferSpecs, HdBufferArrayUsageHint());
575 _sharedData.barContainer.Set(
576 drawItem->GetDrawingCoord()->GetVertexPrimvarIndex(), range);
577 }
578
579 // Let HdSt_VolumeShader know about the points bar so that it
580 // can fill it with the vertices of the volume bounding box.
581 materialNetworkShader->SetPointsBar(drawItem->GetVertexPrimvarRange());
582
583 // If HdSt_VolumeShader is not in charge of filling the points bar
584 // from the volume bounding box computed from the fields, ...
585 if (!materialNetworkShader->GetFillsPointsBar()) {
586 // ... fill the points from the authored extents.
587 resourceRegistry->AddSource(
588 drawItem->GetVertexPrimvarRange(),
589 std::make_shared<HdVtBufferSource>(
590 HdTokens->points,
591 _ComputeBBoxVertices(
592 _sharedData.bounds.GetRange())));
593 }
594 }
595
596 if ((*dirtyBits) & HdChangeTracker::NewRepr) {
597 // Bounding box topology and geometric shader key only need to
598 // be initialized the first time we make the draw item.
599
600 const HdSt_VolumeShaderKey shaderKey;
601 drawItem->SetGeometricShader(
602 HdSt_GeometricShader::Create(shaderKey, resourceRegistry));
603
604 /* TRIANGLE INDICES */
605 {
606 // XXX:
607 // Always the same triangle indices, should they be allocated only
608 // once and shared across all volumes?
609 HdBufferSourceSharedPtr const source =
610 std::make_shared<HdVtBufferSource>(
611 HdTokens->indices, _GetCubeTriangleIndices());
612
613 HdBufferSourceSharedPtrVector sources = { source };
614
615 if (!HdStIsValidBAR(drawItem->GetTopologyRange())) {
616 HdBufferSpecVector bufferSpecs;
617 HdBufferSpec::GetBufferSpecs(sources, &bufferSpecs);
618
619 HdBufferArrayRangeSharedPtr const range =
620 resourceRegistry->AllocateNonUniformBufferArrayRange(
621 HdTokens->primvar, bufferSpecs,
622 HdBufferArrayUsageHint());
623 _sharedData.barContainer.Set(
624 drawItem->GetDrawingCoord()->GetTopologyIndex(), range);
625 }
626
627 resourceRegistry->AddSources(drawItem->GetTopologyRange(),
628 std::move(sources));
629 }
630 }
631 }
632
633 PXR_NAMESPACE_CLOSE_SCOPE
634