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/vboMemoryManager.h"
25 
26 #include "pxr/imaging/hdSt/bufferResource.h"
27 #include "pxr/imaging/hdSt/glUtils.h"
28 #include "pxr/imaging/hdSt/resourceRegistry.h"
29 #include "pxr/imaging/hdSt/tokens.h"
30 #include "pxr/imaging/hd/perfLog.h"
31 #include "pxr/imaging/hd/tokens.h"
32 
33 #include "pxr/imaging/hf/perfLog.h"
34 
35 #include "pxr/imaging/hgi/hgi.h"
36 #include "pxr/imaging/hgi/blitCmds.h"
37 #include "pxr/imaging/hgi/blitCmdsOps.h"
38 #include "pxr/imaging/hgi/buffer.h"
39 
40 #include "pxr/base/arch/hash.h"
41 #include "pxr/base/tf/diagnostic.h"
42 #include "pxr/base/tf/envSetting.h"
43 #include "pxr/base/tf/iterator.h"
44 #include "pxr/base/tf/enum.h"
45 
46 #include <vector>
47 
48 PXR_NAMESPACE_OPEN_SCOPE
49 
50 
51 TF_DEFINE_ENV_SETTING(HD_MAX_VBO_SIZE, (1*1024*1024*1024),
52                       "Maximum aggregated VBO size");
53 
54 // ---------------------------------------------------------------------------
55 //  HdStVBOMemoryManager
56 // ---------------------------------------------------------------------------
57 HdBufferArraySharedPtr
CreateBufferArray(TfToken const & role,HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint)58 HdStVBOMemoryManager::CreateBufferArray(
59     TfToken const &role,
60     HdBufferSpecVector const &bufferSpecs,
61     HdBufferArrayUsageHint usageHint)
62 {
63     return std::make_shared<HdStVBOMemoryManager::_StripedBufferArray>(
64         _resourceRegistry, role, bufferSpecs, usageHint);
65 }
66 
67 
68 HdBufferArrayRangeSharedPtr
CreateBufferArrayRange()69 HdStVBOMemoryManager::CreateBufferArrayRange()
70 {
71     return std::make_shared<_StripedBufferArrayRange>(_resourceRegistry);
72 }
73 
74 
75 HdAggregationStrategy::AggregationId
ComputeAggregationId(HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint) const76 HdStVBOMemoryManager::ComputeAggregationId(
77     HdBufferSpecVector const &bufferSpecs,
78     HdBufferArrayUsageHint usageHint) const
79 {
80     static size_t salt = ArchHash(__FUNCTION__, sizeof(__FUNCTION__));
81     size_t result = salt;
82     for (HdBufferSpec const &spec : bufferSpecs) {
83         boost::hash_combine(result, spec.Hash());
84     }
85 
86     boost::hash_combine(result, usageHint.value);
87 
88     // promote to size_t
89     return (AggregationId)result;
90 }
91 
92 
93 /// Returns the buffer specs from a given buffer array
94 HdBufferSpecVector
GetBufferSpecs(HdBufferArraySharedPtr const & bufferArray) const95 HdStVBOMemoryManager::GetBufferSpecs(
96     HdBufferArraySharedPtr const &bufferArray) const
97 {
98     _StripedBufferArraySharedPtr bufferArray_ =
99         std::static_pointer_cast<_StripedBufferArray> (bufferArray);
100     return bufferArray_->GetBufferSpecs();
101 }
102 
103 
104 /// Returns the size of the GPU memory used by the passed buffer array
105 size_t
GetResourceAllocation(HdBufferArraySharedPtr const & bufferArray,VtDictionary & result) const106 HdStVBOMemoryManager::GetResourceAllocation(
107     HdBufferArraySharedPtr const &bufferArray,
108     VtDictionary &result) const
109 {
110     std::set<uint64_t> idSet;
111     size_t gpuMemoryUsed = 0;
112 
113     _StripedBufferArraySharedPtr bufferArray_ =
114         std::static_pointer_cast<_StripedBufferArray> (bufferArray);
115 
116     TF_FOR_ALL(resIt, bufferArray_->GetResources()) {
117         HdStBufferResourceSharedPtr const & resource = resIt->second;
118         HgiBufferHandle buffer = resource->GetHandle();
119 
120         // XXX avoid double counting of resources shared within a buffer
121         uint64_t id = buffer ? buffer->GetRawResource() : 0;
122         if (idSet.count(id) == 0) {
123             idSet.insert(id);
124 
125             std::string const & role = resource->GetRole().GetString();
126             size_t size = size_t(resource->GetSize());
127 
128             if (result.count(role)) {
129                 size_t currentSize = result[role].Get<size_t>();
130                 result[role] = VtValue(currentSize + size);
131             } else {
132                 result[role] = VtValue(size);
133             }
134 
135             gpuMemoryUsed += size;
136         }
137     }
138 
139     return gpuMemoryUsed;
140 }
141 
142 
143 // ---------------------------------------------------------------------------
144 //  _StripedBufferArray
145 // ---------------------------------------------------------------------------
_StripedBufferArray(HdStResourceRegistry * resourceRegistry,TfToken const & role,HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint)146 HdStVBOMemoryManager::_StripedBufferArray::_StripedBufferArray(
147     HdStResourceRegistry* resourceRegistry,
148     TfToken const &role,
149     HdBufferSpecVector const &bufferSpecs,
150     HdBufferArrayUsageHint usageHint)
151     : HdBufferArray(role, HdPerfTokens->garbageCollectedVbo, usageHint),
152       _resourceRegistry(resourceRegistry),
153       _needsCompaction(false),
154       _totalCapacity(0),
155       _maxBytesPerElement(0)
156 {
157     HD_TRACE_FUNCTION();
158     HF_MALLOC_TAG_FUNCTION();
159 
160     /*
161        non-interleaved non-uniform buffer array (for example)
162           .------------------------------------------------------.
163      vec3 | pos.x (prim0)         ||  pos.x (prim1)       || ... |
164           |     y                 ||      y               ||     |
165           |     z                 ||      z               ||     |
166           '------------------------------------------------------'
167           .------------------------------------------------------.
168      vec4 | color.r (prim0)       ||  color.r (prim1)     || ... |
169           |       g               ||        g             ||     |
170           |       b               ||        b             ||     |
171           |       a               ||        a             ||     |
172           '------------------------------------------------------'
173            ^--range0.numElements--^^--range1.numElements--^
174                                    |
175            ^-^                     ^--range1.offset
176             stride
177     */
178 
179     // populate BufferResources
180     TF_FOR_ALL(it, bufferSpecs) {
181         int stride = HdDataSizeOfTupleType(it->tupleType);
182         _AddResource(it->name, it->tupleType, /*offset*/0, stride);
183     }
184 
185     // VBO Memory Manage supports an effectivly limitless set of ranges
186     _SetMaxNumRanges(std::numeric_limits<size_t>::max());
187 
188     // compute max bytes / elements
189     TF_FOR_ALL (it, GetResources()) {
190         _maxBytesPerElement = std::max(
191             _maxBytesPerElement,
192             HdDataSizeOfTupleType(it->second->GetTupleType()));
193     }
194 
195     // GetMaxNumElements() will crash with a divide by 0
196     // error if _maxBytesPerElement is 0.
197     //
198     // This can happen if bufferSpecs was empty and thus
199     // no resources were added.   If means something went
200     // wrong earlier and we are just trying to survive.
201     if (!TF_VERIFY(_maxBytesPerElement != 0))
202     {
203         _maxBytesPerElement = 1;
204     }
205 }
206 
207 HdStBufferResourceSharedPtr
_AddResource(TfToken const & name,HdTupleType tupleType,int offset,int stride)208 HdStVBOMemoryManager::_StripedBufferArray::_AddResource(TfToken const& name,
209                                                    HdTupleType tupleType,
210                                                    int offset,
211                                                    int stride)
212 {
213     HD_TRACE_FUNCTION();
214     if (TfDebug::IsEnabled(HD_SAFE_MODE)) {
215         // duplication check
216         HdStBufferResourceSharedPtr bufferRes = GetResource(name);
217         if (!TF_VERIFY(!bufferRes)) {
218             return bufferRes;
219         }
220     }
221 
222     HdStBufferResourceSharedPtr bufferRes =
223         std::make_shared<HdStBufferResource>(
224             GetRole(), tupleType, offset, stride);
225     _resourceList.emplace_back(name, bufferRes);
226     return bufferRes;
227 }
228 
~_StripedBufferArray()229 HdStVBOMemoryManager::_StripedBufferArray::~_StripedBufferArray()
230 {
231     HD_TRACE_FUNCTION();
232     HF_MALLOC_TAG_FUNCTION();
233 
234     // invalidate buffer array ranges in range list
235     // (these ranges may still be held by drawItems)
236     size_t rangeCount = GetRangeCount();
237     for (size_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
238         _StripedBufferArrayRangeSharedPtr range = _GetRangeSharedPtr(rangeIdx);
239 
240         if (range) {
241             range->Invalidate();
242         }
243     }
244 }
245 
246 
247 bool
GarbageCollect()248 HdStVBOMemoryManager::_StripedBufferArray::GarbageCollect()
249 {
250     HD_TRACE_FUNCTION();
251     HF_MALLOC_TAG_FUNCTION();
252 
253     if (_needsCompaction) {
254         RemoveUnusedRanges();
255 
256         std::vector<HdBufferArrayRangeSharedPtr> ranges;
257         size_t rangeCount = GetRangeCount();
258         ranges.reserve(rangeCount);
259         for (size_t i = 0; i < rangeCount; ++i) {
260             HdBufferArrayRangeSharedPtr range = GetRange(i).lock();
261             if (range)
262                 ranges.push_back(range);
263         }
264         Reallocate(ranges, shared_from_this());
265     }
266 
267     if (GetRangeCount() == 0) {
268         _DeallocateResources();
269         return true;
270     }
271     return false;
272 }
273 
274 void
Reallocate(std::vector<HdBufferArrayRangeSharedPtr> const & ranges,HdBufferArraySharedPtr const & curRangeOwner)275 HdStVBOMemoryManager::_StripedBufferArray::Reallocate(
276     std::vector<HdBufferArrayRangeSharedPtr> const &ranges,
277     HdBufferArraySharedPtr const &curRangeOwner)
278 {
279     HD_TRACE_FUNCTION();
280     HF_MALLOC_TAG_FUNCTION();
281 
282     HD_PERF_COUNTER_INCR(HdPerfTokens->vboRelocated);
283 
284     _StripedBufferArraySharedPtr curRangeOwner_ =
285         std::static_pointer_cast<_StripedBufferArray> (curRangeOwner);
286 
287     if (!TF_VERIFY(GetResources().size() ==
288                       curRangeOwner_->GetResources().size())) {
289         TF_CODING_ERROR("Resource mismatch when reallocating buffer array");
290         return;
291     }
292 
293     if (TfDebug::IsEnabled(HD_SAFE_MODE)) {
294         HdStBufferResourceNamedList::size_type bresIdx = 0;
295         TF_FOR_ALL(bresIt, GetResources()) {
296             TF_VERIFY(curRangeOwner_->GetResources()[bresIdx++].second ==
297                       curRangeOwner_->GetResource(bresIt->first));
298         }
299     }
300 
301     // count up total elements and update new offsets
302     size_t totalNumElements = 0;
303     std::vector<size_t> newOffsets;
304     newOffsets.reserve(ranges.size());
305 
306     TF_FOR_ALL (it, ranges) {
307         HdBufferArrayRangeSharedPtr const &range = *it;
308         if (!range) {
309             TF_CODING_ERROR("Expired range found in the reallocation list");
310             continue;
311         }
312 
313         // save new offset
314         newOffsets.push_back(totalNumElements);
315 
316         // XXX: always tightly pack for now.
317         totalNumElements += range->GetNumElements();
318     }
319 
320     // update range list (should be done before early exit)
321     _SetRangeList(ranges);
322 
323     _totalCapacity = totalNumElements;
324 
325     Hgi* hgi = _resourceRegistry->GetHgi();
326     HgiBlitCmds* blitCmds = _resourceRegistry->GetGlobalBlitCmds();
327     blitCmds->PushDebugGroup(__ARCH_PRETTY_FUNCTION__);
328 
329     // resize each BufferResource
330     HdStBufferResourceNamedList const& resources = GetResources();
331     for (size_t bresIdx=0; bresIdx<resources.size(); ++bresIdx) {
332         HdStBufferResourceSharedPtr const &bres = resources[bresIdx].second;
333         HdStBufferResourceSharedPtr const &curRes =
334                 curRangeOwner_->GetResources()[bresIdx].second;
335 
336         int bytesPerElement = HdDataSizeOfTupleType(bres->GetTupleType());
337         TF_VERIFY(bytesPerElement > 0);
338         size_t bufferSize = bytesPerElement * _totalCapacity;
339 
340         // allocate new one
341         // curBuf and oldBuf will be different when we are adopting ranges
342         // from another buffer array.
343         HgiBufferHandle& oldBuf = bres->GetHandle();
344         HgiBufferHandle& curBuf = curRes->GetHandle();
345         HgiBufferHandle newBuf;
346 
347         // Skip buffers of zero size
348         if (bufferSize > 0) {
349             HgiBufferDesc bufDesc;
350             bufDesc.usage = HgiBufferUsageUniform;
351             bufDesc.byteSize = bufferSize;
352             newBuf = hgi->CreateBuffer(bufDesc);
353         }
354 
355         // if old and new buffer exist, copy unchanged data
356         if (curBuf && newBuf) {
357             std::vector<size_t>::iterator newOffsetIt = newOffsets.begin();
358 
359             // pre-pass to combine consecutive buffer range relocation
360             HdStBufferRelocator relocator(curBuf, newBuf);
361             TF_FOR_ALL (it, ranges) {
362                 _StripedBufferArrayRangeSharedPtr range =
363                     std::static_pointer_cast<_StripedBufferArrayRange>(*it);
364                 if (!range) {
365                     TF_CODING_ERROR("_StripedBufferArrayRange "
366                                     "expired unexpectedly.");
367                     continue;
368                 }
369 
370                 // copy the range. There are three cases:
371                 //
372                 // 1. src length (capacity) == dst length (numElements)
373                 //   Copy the entire range
374                 //
375                 // 2. src length < dst length
376                 //   Enlarging the range. This typically happens when
377                 //   applying quadrangulation/subdivision to populate
378                 //   additional data at the end of source data.
379                 //
380                 // 3. src length > dst length
381                 //   Shrinking the range. When the garbage collection
382                 //   truncates ranges.
383                 //
384                 int oldSize = range->GetCapacity();
385                 int newSize = range->GetNumElements();
386                 ptrdiff_t copySize =
387                     std::min(oldSize, newSize) * bytesPerElement;
388                 int oldOffset = range->GetElementOffset();
389                 if (copySize > 0) {
390                     ptrdiff_t readOffset = oldOffset * bytesPerElement;
391                     ptrdiff_t writeOffset = *newOffsetIt * bytesPerElement;
392 
393                     relocator.AddRange(readOffset, writeOffset, copySize);
394                 }
395                 ++newOffsetIt;
396             }
397 
398             // buffer copy
399             relocator.Commit(blitCmds);
400         }
401         if (oldBuf) {
402             // delete old buffer
403             hgi->DestroyBuffer(&oldBuf);
404         }
405 
406         // update allocation of buffer resource
407         bres->SetAllocation(newBuf, bufferSize);
408     }
409 
410     // update ranges
411     for (size_t idx = 0; idx < ranges.size(); ++idx) {
412         _StripedBufferArrayRangeSharedPtr range =
413             std::static_pointer_cast<_StripedBufferArrayRange>(ranges[idx]);
414         if (!range) {
415             TF_CODING_ERROR("_StripedBufferArrayRange expired unexpectedly.");
416             continue;
417         }
418         range->SetElementOffset(newOffsets[idx]);
419         range->SetCapacity(range->GetNumElements());
420     }
421 
422     blitCmds->PopDebugGroup();
423 
424     _needsReallocation = false;
425     _needsCompaction = false;
426 
427     // increment version to rebuild dispatch buffers.
428     IncrementVersion();
429 }
430 
431 void
_DeallocateResources()432 HdStVBOMemoryManager::_StripedBufferArray::_DeallocateResources()
433 {
434     Hgi* hgi = _resourceRegistry->GetHgi();
435     TF_FOR_ALL (it, GetResources()) {
436         hgi->DestroyBuffer(&it->second->GetHandle());
437     }
438 }
439 
440 /*virtual*/
441 size_t
GetMaxNumElements() const442 HdStVBOMemoryManager::_StripedBufferArray::GetMaxNumElements() const
443 {
444     static size_t vboMaxSize = TfGetEnvSetting(HD_MAX_VBO_SIZE);
445     return vboMaxSize / _maxBytesPerElement;
446 }
447 
448 void
DebugDump(std::ostream & out) const449 HdStVBOMemoryManager::_StripedBufferArray::DebugDump(std::ostream &out) const
450 {
451     out << "  HdStVBOMemoryManager\n";
452     out << "  total capacity = " << _totalCapacity << "\n";
453     out << "    Range entries " << GetRangeCount() << ":\n";
454 
455     size_t rangeCount = GetRangeCount();
456     for (size_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
457         _StripedBufferArrayRangeSharedPtr range = _GetRangeSharedPtr(rangeIdx);
458         if (range) {
459             out << "      " << rangeIdx << *range;
460         }
461     }
462 }
463 
464 HdStBufferResourceSharedPtr
GetResource() const465 HdStVBOMemoryManager::_StripedBufferArray::GetResource() const
466 {
467     HD_TRACE_FUNCTION();
468 
469     if (_resourceList.empty()) return HdStBufferResourceSharedPtr();
470 
471     if (TfDebug::IsEnabled(HD_SAFE_MODE)) {
472         // make sure this buffer array has only one resource.
473         HgiBufferHandle const& buffer =
474                 _resourceList.begin()->second->GetHandle();
475         TF_FOR_ALL (it, _resourceList) {
476             if (it->second->GetHandle() != buffer) {
477                 TF_CODING_ERROR("GetResource(void) called on"
478                                 "HdBufferArray having multiple GPU resources");
479             }
480         }
481     }
482 
483     // returns the first item
484     return _resourceList.begin()->second;
485 }
486 
487 HdStBufferResourceSharedPtr
GetResource(TfToken const & name)488 HdStVBOMemoryManager::_StripedBufferArray::GetResource(TfToken const& name)
489 {
490     HD_TRACE_FUNCTION();
491 
492     // linear search.
493     // The number of buffer resources should be small (<10 or so).
494     for (HdStBufferResourceNamedList::iterator it = _resourceList.begin();
495          it != _resourceList.end(); ++it) {
496         if (it->first == name) return it->second;
497     }
498     return HdStBufferResourceSharedPtr();
499 }
500 
501 HdBufferSpecVector
GetBufferSpecs() const502 HdStVBOMemoryManager::_StripedBufferArray::GetBufferSpecs() const
503 {
504     HdBufferSpecVector result;
505     result.reserve(_resourceList.size());
506     TF_FOR_ALL (it, _resourceList) {
507         result.emplace_back(it->first, it->second->GetTupleType());
508     }
509     return result;
510 }
511 
512 // ---------------------------------------------------------------------------
513 //  _StripedBufferArrayRange
514 // ---------------------------------------------------------------------------
~_StripedBufferArrayRange()515 HdStVBOMemoryManager::_StripedBufferArrayRange::~_StripedBufferArrayRange()
516 {
517     // Notify that hosting buffer array needs to be garbage collected.
518     //
519     // Don't do any substantial work here.
520     //
521     if (_stripedBufferArray) {
522         _stripedBufferArray->SetNeedsCompaction();
523 
524         // notify source bufferArray to bump the version so that
525         // drawbatches to be rebuilt.
526         // Also note that the buffer migration takes place only in
527         // this StripedBufferArray, not in other InterleavedVBO/SimpleVBO.
528         _stripedBufferArray->IncrementVersion();
529     }
530 }
531 
532 
533 bool
IsAssigned() const534 HdStVBOMemoryManager::_StripedBufferArrayRange::IsAssigned() const
535 {
536     return (_stripedBufferArray != nullptr);
537 }
538 
539 bool
IsImmutable() const540 HdStVBOMemoryManager::_StripedBufferArrayRange::IsImmutable() const
541 {
542     return (_stripedBufferArray != nullptr)
543          && _stripedBufferArray->IsImmutable();
544 }
545 
546 bool
RequiresStaging() const547 HdStVBOMemoryManager::_StripedBufferArrayRange::RequiresStaging() const
548 {
549     return false;
550 }
551 
552 bool
Resize(int numElements)553 HdStVBOMemoryManager::_StripedBufferArrayRange::Resize(int numElements)
554 {
555     HD_TRACE_FUNCTION();
556     HF_MALLOC_TAG_FUNCTION();
557 
558     if (!TF_VERIFY(_stripedBufferArray)) return false;
559 
560     bool needsReallocation = false;
561 
562     // XXX: varying topology points fix (bug 114080)
563     //
564     // MDI draw uses a dispatch buffer, and it includes numElements to be
565     // drawn. When a topology is varying, numElements will change so the
566     // dispatch buffer has to be rebuilt. Currently we depend on entire
567     // buffer reallocation for index-drawing prims (e.g. meshes and curves)
568     // with varying topology. We always allocate new BARs for them,
569     // which is inefficient, and will be addressed later (bug 103767)
570     //
571     // However varying points have another problem: When it reduces its
572     // number of points, it doesn't cause the reallocation in the below code
573     // (disabled by #if 0) since points don't have an index buffer.
574     //
575     // These two problems have to be solved together by introducing more
576     // robust mechanism which updates dispatch buffer partially to
577     // reflect numElements correctly without having reallocation.
578     // It needs more works, until then, we invoke reallocation whenever
579     // numElements changes in an aggregated buffer, for the correctness
580     // problem of points drawing (this is bug 114080).
581     //
582     // The varying mesh batch may suffer a performance regression
583     // from this treatment, but it should be relatively small. Because the
584     // topology buffer has already been reallocated on every changes as
585     // described above and the primvar buffer is also reallocated in
586     // GarbageCollect() before drawing (see HdEngine::Draw()).
587     //
588     // We need to revisit to clean this up soon.
589     //
590 #if 0
591     if (_capacity < numElements) {
592         // mark entire buffer array to be relocated
593         _stripedBufferArray->SetNeedsReallocation();
594         needsReallocation = true;
595     } else if (_capacity > numElements) {
596         // mark the buffer array can be compacted
597         _stripedBufferArray->SetNeedsCompaction();
598     }
599 #else
600     if (_capacity != numElements) {
601         const size_t numMaxElements = GetMaxNumElements();
602 
603         if (static_cast<size_t>(numElements) > numMaxElements) {
604             TF_WARN("Attempting to resize the BAR with 0x%x elements when the "
605                     "max number of elements in the buffer array is 0x%lx. "
606                     "Clamping BAR size to the latter.",
607                      numElements, numMaxElements);
608 
609             numElements = numMaxElements;
610         }
611         _stripedBufferArray->SetNeedsReallocation();
612         needsReallocation = true;
613     }
614 #endif
615 
616     _numElements = numElements;
617     return needsReallocation;
618 }
619 
620 void
CopyData(HdBufferSourceSharedPtr const & bufferSource)621 HdStVBOMemoryManager::_StripedBufferArrayRange::CopyData(
622     HdBufferSourceSharedPtr const &bufferSource)
623 {
624     HD_TRACE_FUNCTION();
625     HF_MALLOC_TAG_FUNCTION();
626 
627     if (!TF_VERIFY(_stripedBufferArray)) return;
628 
629     HdStBufferResourceSharedPtr VBO =
630         _stripedBufferArray->GetResource(bufferSource->GetName());
631 
632     if (!TF_VERIFY((VBO && VBO->GetHandle()),
633                       "VBO doesn't exist for %s",
634                       bufferSource->GetName().GetText())) {
635         return;
636     }
637 
638     // datatype of bufferSource has to match with bufferResource
639     if (!TF_VERIFY(bufferSource->GetTupleType() == VBO->GetTupleType(),
640                    "'%s': (%s (%i) x %zu) != (%s (%i) x %zu)\n",
641                    bufferSource->GetName().GetText(),
642                    TfEnum::GetName(bufferSource->GetTupleType().type).c_str(),
643                    bufferSource->GetTupleType().type,
644                    bufferSource->GetTupleType().count,
645                    TfEnum::GetName(VBO->GetTupleType().type).c_str(),
646                    VBO->GetTupleType().type,
647                    VBO->GetTupleType().count)) {
648         return;
649     }
650 
651     int bytesPerElement = HdDataSizeOfTupleType(VBO->GetTupleType());
652 
653     // overrun check. for graceful handling of erroneous assets,
654     // issue warning here and continue to copy for the valid range.
655     size_t dstSize = _numElements * bytesPerElement;
656     size_t srcSize =
657         bufferSource->GetNumElements() *
658         HdDataSizeOfTupleType(bufferSource->GetTupleType());
659     if (srcSize > dstSize) {
660         TF_WARN("%s: size %ld is larger than the range (%ld)",
661                 bufferSource->GetName().GetText(), srcSize, dstSize);
662         srcSize = dstSize;
663     }
664     size_t vboOffset = bytesPerElement * _elementOffset;
665 
666     HD_PERF_COUNTER_INCR(HdStPerfTokens->copyBufferCpuToGpu);
667 
668     HgiBufferCpuToGpuOp blitOp;
669     blitOp.cpuSourceBuffer = bufferSource->GetData();
670     blitOp.gpuDestinationBuffer = VBO->GetHandle();
671 
672     blitOp.sourceByteOffset = 0;
673     blitOp.byteSize = srcSize;
674     blitOp.destinationByteOffset = vboOffset;
675 
676     HgiBlitCmds* blitCmds = GetResourceRegistry()->GetGlobalBlitCmds();
677     blitCmds->PushDebugGroup(__ARCH_PRETTY_FUNCTION__);
678     blitCmds->CopyBufferCpuToGpu(blitOp);
679     blitCmds->PopDebugGroup();
680 }
681 
682 int
GetByteOffset(TfToken const & resourceName) const683 HdStVBOMemoryManager::_StripedBufferArrayRange::GetByteOffset(
684     TfToken const& resourceName) const
685 {
686     if (!TF_VERIFY(_stripedBufferArray)) return 0;
687     HdStBufferResourceSharedPtr VBO =
688         _stripedBufferArray->GetResource(resourceName);
689 
690     if (!VBO || (!VBO->GetHandle() && _numElements > 0)) {
691         TF_CODING_ERROR("VBO doesn't exist for %s", resourceName.GetText());
692         return 0;
693     }
694 
695     return (int) _GetByteOffset(VBO);
696 }
697 
698 VtValue
ReadData(TfToken const & name) const699 HdStVBOMemoryManager::_StripedBufferArrayRange::ReadData(TfToken const &name) const
700 {
701     HD_TRACE_FUNCTION();
702     HF_MALLOC_TAG_FUNCTION();
703 
704     VtValue result;
705     if (!TF_VERIFY(_stripedBufferArray)) return result;
706 
707     HdStBufferResourceSharedPtr VBO = _stripedBufferArray->GetResource(name);
708 
709     if (!VBO || (!VBO->GetHandle() && _numElements > 0)) {
710         TF_CODING_ERROR("VBO doesn't exist for %s", name.GetText());
711         return result;
712     }
713 
714     size_t vboOffset = _GetByteOffset(VBO);
715 
716     uint64_t vbo = VBO->GetHandle() ? VBO->GetHandle()->GetRawResource() : 0;
717 
718     result = HdStGLUtils::ReadBuffer(vbo,
719                                    VBO->GetTupleType(),
720                                    vboOffset,
721                                    /*stride=*/0, // not interleaved.
722                                    _numElements);
723 
724     return result;
725 }
726 
727 size_t
GetMaxNumElements() const728 HdStVBOMemoryManager::_StripedBufferArrayRange::GetMaxNumElements() const
729 {
730     return _stripedBufferArray->GetMaxNumElements();
731 }
732 
733 HdBufferArrayUsageHint
GetUsageHint() const734 HdStVBOMemoryManager::_StripedBufferArrayRange::GetUsageHint() const
735 {
736     if (!TF_VERIFY(_stripedBufferArray)) {
737         return HdBufferArrayUsageHint();
738     }
739 
740     return _stripedBufferArray->GetUsageHint();
741 }
742 
743 
744 HdStBufferResourceSharedPtr
GetResource() const745 HdStVBOMemoryManager::_StripedBufferArrayRange::GetResource() const
746 {
747     if (!TF_VERIFY(_stripedBufferArray)) return HdStBufferResourceSharedPtr();
748 
749     return _stripedBufferArray->GetResource();
750 }
751 
752 HdStBufferResourceSharedPtr
GetResource(TfToken const & name)753 HdStVBOMemoryManager::_StripedBufferArrayRange::GetResource(TfToken const& name)
754 {
755     if (!TF_VERIFY(_stripedBufferArray)) return HdStBufferResourceSharedPtr();
756 
757     return _stripedBufferArray->GetResource(name);
758 }
759 
760 HdStBufferResourceNamedList const&
GetResources() const761 HdStVBOMemoryManager::_StripedBufferArrayRange::GetResources() const
762 {
763     if (!TF_VERIFY(_stripedBufferArray)) {
764         static HdStBufferResourceNamedList empty;
765         return empty;
766     }
767     return _stripedBufferArray->GetResources();
768 }
769 
770 void
SetBufferArray(HdBufferArray * bufferArray)771 HdStVBOMemoryManager::_StripedBufferArrayRange::SetBufferArray(HdBufferArray *bufferArray)
772 {
773     _stripedBufferArray = static_cast<_StripedBufferArray *>(bufferArray);
774 }
775 
776 void
DebugDump(std::ostream & out) const777 HdStVBOMemoryManager::_StripedBufferArrayRange::DebugDump(std::ostream &out) const
778 {
779     out << "[StripedBAR] offset = " << _elementOffset
780         << ", numElements = " << _numElements
781         << ", capacity = " << _capacity
782         << "\n";
783 }
784 
785 const void *
_GetAggregation() const786 HdStVBOMemoryManager::_StripedBufferArrayRange::_GetAggregation() const
787 {
788     return _stripedBufferArray;
789 }
790 
791 size_t
_GetByteOffset(HdStBufferResourceSharedPtr const & resource) const792 HdStVBOMemoryManager::_StripedBufferArrayRange::_GetByteOffset(
793     HdStBufferResourceSharedPtr const& resource) const
794 {
795     return HdDataSizeOfTupleType(resource->GetTupleType()) * _elementOffset;
796 }
797 
798 PXR_NAMESPACE_CLOSE_SCOPE
799 
800