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/base/tf/diagnostic.h"
25 #include "pxr/base/tf/envSetting.h"
26 #include "pxr/base/tf/iterator.h"
27 
28 #include "pxr/imaging/hdSt/bufferResource.h"
29 #include "pxr/imaging/hdSt/glUtils.h"
30 #include "pxr/imaging/hdSt/tokens.h"
31 #include "pxr/imaging/hdSt/vboSimpleMemoryManager.h"
32 
33 #include "pxr/imaging/hd/bufferArrayRange.h"
34 #include "pxr/imaging/hd/bufferSource.h"
35 #include "pxr/imaging/hd/perfLog.h"
36 #include "pxr/imaging/hd/tokens.h"
37 
38 #include "pxr/imaging/hgi/hgi.h"
39 #include "pxr/imaging/hgi/blitCmds.h"
40 #include "pxr/imaging/hgi/blitCmdsOps.h"
41 #include "pxr/imaging/hgi/buffer.h"
42 
43 #include "pxr/imaging/hf/perfLog.h"
44 
45 #include <atomic>
46 
47 #include <boost/functional/hash.hpp>
48 
49 PXR_NAMESPACE_OPEN_SCOPE
50 
51 
52 extern TfEnvSetting<int> HD_MAX_VBO_SIZE;
53 
54 // ---------------------------------------------------------------------------
55 //  HdStVBOSimpleMemoryManager
56 // ---------------------------------------------------------------------------
57 
58 HdBufferArraySharedPtr
CreateBufferArray(TfToken const & role,HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint)59 HdStVBOSimpleMemoryManager::CreateBufferArray(
60     TfToken const &role,
61     HdBufferSpecVector const &bufferSpecs,
62     HdBufferArrayUsageHint usageHint)
63 {
64     return std::make_shared<HdStVBOSimpleMemoryManager::_SimpleBufferArray>(
65         _resourceRegistry, role, bufferSpecs, usageHint);
66 }
67 
68 HdBufferArrayRangeSharedPtr
CreateBufferArrayRange()69 HdStVBOSimpleMemoryManager::CreateBufferArrayRange()
70 {
71     return std::make_shared<HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange>
72                 (_resourceRegistry);
73 }
74 
75 HdAggregationStrategy::AggregationId
ComputeAggregationId(HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint) const76 HdStVBOSimpleMemoryManager::ComputeAggregationId(
77     HdBufferSpecVector const &bufferSpecs,
78     HdBufferArrayUsageHint usageHint) const
79 {
80     // Always returns different value
81     static std::atomic_uint id(0);
82 
83     AggregationId hash = id++;  // Atomic
84 
85     return hash;
86 }
87 
88 /// Returns the buffer specs from a given buffer array
89 HdBufferSpecVector
GetBufferSpecs(HdBufferArraySharedPtr const & bufferArray) const90 HdStVBOSimpleMemoryManager::GetBufferSpecs(
91     HdBufferArraySharedPtr const &bufferArray) const
92 {
93     _SimpleBufferArraySharedPtr bufferArray_ =
94         std::static_pointer_cast<_SimpleBufferArray> (bufferArray);
95     return bufferArray_->GetBufferSpecs();
96 }
97 
98 /// Returns the size of the GPU memory used by the passed buffer array
99 size_t
GetResourceAllocation(HdBufferArraySharedPtr const & bufferArray,VtDictionary & result) const100 HdStVBOSimpleMemoryManager::GetResourceAllocation(
101     HdBufferArraySharedPtr const &bufferArray,
102     VtDictionary &result) const
103 {
104     std::set<uint64_t> idSet;
105     size_t gpuMemoryUsed = 0;
106 
107     _SimpleBufferArraySharedPtr bufferArray_ =
108         std::static_pointer_cast<_SimpleBufferArray> (bufferArray);
109 
110     TF_FOR_ALL(resIt, bufferArray_->GetResources()) {
111         HdStBufferResourceSharedPtr const & resource = resIt->second;
112 
113         // XXX Reallocate inserts an empty (invalid) handle for empty buffers.
114         HgiBufferHandle buffer = resource->GetHandle();
115         uint64_t id = buffer ? buffer->GetRawResource() : 0;
116 
117         // XXX avoid double counting of resources shared within a buffer
118         if (id > 0 && idSet.count(id) == 0) {
119             idSet.insert(id);
120 
121             std::string const & role = resource->GetRole().GetString();
122             size_t size = size_t(resource->GetSize());
123 
124             if (result.count(role)) {
125                 size_t currentSize = result[role].Get<size_t>();
126                 result[role] = VtValue(currentSize + size);
127             } else {
128                 result[role] = VtValue(size);
129             }
130 
131             gpuMemoryUsed += size;
132         }
133     }
134 
135     return gpuMemoryUsed;
136 }
137 
138 // ---------------------------------------------------------------------------
139 //  _SimpleBufferArray
140 // ---------------------------------------------------------------------------
_SimpleBufferArray(HdStResourceRegistry * resourceRegistry,TfToken const & role,HdBufferSpecVector const & bufferSpecs,HdBufferArrayUsageHint usageHint)141 HdStVBOSimpleMemoryManager::_SimpleBufferArray::_SimpleBufferArray(
142     HdStResourceRegistry* resourceRegistry,
143     TfToken const &role,
144     HdBufferSpecVector const &bufferSpecs,
145     HdBufferArrayUsageHint usageHint)
146  : HdBufferArray(role, TfToken(), usageHint)
147  , _resourceRegistry(resourceRegistry)
148  , _capacity(0)
149  , _maxBytesPerElement(0)
150 {
151     HD_TRACE_FUNCTION();
152     HF_MALLOC_TAG_FUNCTION();
153 
154     // populate BufferResources
155     TF_FOR_ALL(it, bufferSpecs) {
156         int stride = HdDataSizeOfTupleType(it->tupleType);
157         _AddResource(it->name, it->tupleType, /*offset=*/0, stride);
158     }
159 
160     _SetMaxNumRanges(1);
161 
162     // compute max bytes / elements
163     TF_FOR_ALL (it, GetResources()) {
164         HdStBufferResourceSharedPtr const &bres = it->second;
165         _maxBytesPerElement = std::max(
166             _maxBytesPerElement,
167             HdDataSizeOfTupleType(bres->GetTupleType()));
168     }
169 }
170 
171 HdStBufferResourceSharedPtr
_AddResource(TfToken const & name,HdTupleType tupleType,int offset,int stride)172 HdStVBOSimpleMemoryManager::_SimpleBufferArray::_AddResource(
173     TfToken const& name,
174     HdTupleType tupleType,
175                             int offset,
176                             int stride)
177 {
178     HD_TRACE_FUNCTION();
179     if (TfDebug::IsEnabled(HD_SAFE_MODE)) {
180         // duplication check
181         HdStBufferResourceSharedPtr bufferRes = GetResource(name);
182         if (!TF_VERIFY(!bufferRes)) {
183             return bufferRes;
184         }
185     }
186 
187     HdStBufferResourceSharedPtr bufferRes =
188         std::make_shared<HdStBufferResource>(
189             GetRole(), tupleType, offset, stride);
190     _resourceList.emplace_back(name, bufferRes);
191     return bufferRes;
192 }
193 
~_SimpleBufferArray()194 HdStVBOSimpleMemoryManager::_SimpleBufferArray::~_SimpleBufferArray()
195 {
196     HD_TRACE_FUNCTION();
197     HF_MALLOC_TAG_FUNCTION();
198 
199     // invalidate buffer array range
200     // (the range may still be held by drawItems)
201     _SimpleBufferArrayRangeSharedPtr range = _GetRangeSharedPtr();
202     if (range) {
203         range->Invalidate();
204     }
205 }
206 
207 
208 bool
GarbageCollect()209 HdStVBOSimpleMemoryManager::_SimpleBufferArray::GarbageCollect()
210 {
211     HD_TRACE_FUNCTION();
212     HF_MALLOC_TAG_FUNCTION();
213 
214     // no range referring this buffer = empty
215     if (GetRangeCount() > 0 && GetRange(0).expired()) {
216         _DeallocateResources();
217         HD_PERF_COUNTER_INCR(HdPerfTokens->garbageCollectedVbo);
218         return true;
219     }
220     return false;
221 }
222 
223 void
DebugDump(std::ostream & out) const224 HdStVBOSimpleMemoryManager::_SimpleBufferArray::DebugDump(std::ostream &out) const
225 {
226     out << "  HdStVBOSimpleMemoryManager";
227     out << "  total capacity = " << _capacity << "\n";
228 }
229 
230 bool
Resize(int numElements)231 HdStVBOSimpleMemoryManager::_SimpleBufferArray::Resize(int numElements)
232 {
233     HD_TRACE_FUNCTION();
234     HF_MALLOC_TAG_FUNCTION();
235 
236     // see the comment in
237     // HdStVBOMemoryManager::_StripedBufferArrayRange::Resize(int numElements)
238     // this change is for the unit test consistency.
239     //
240     // if (_capacity < numElements) {
241     if (_capacity != numElements) {
242         _needsReallocation = true;
243         return true;
244     }
245     return false;
246 }
247 
248 void
Reallocate(std::vector<HdBufferArrayRangeSharedPtr> const & ranges,HdBufferArraySharedPtr const & curRangeOwner)249 HdStVBOSimpleMemoryManager::_SimpleBufferArray::Reallocate(
250     std::vector<HdBufferArrayRangeSharedPtr> const & ranges,
251     HdBufferArraySharedPtr const &curRangeOwner)
252 {
253     HD_TRACE_FUNCTION();
254     HF_MALLOC_TAG_FUNCTION();
255 
256     HD_PERF_COUNTER_INCR(HdPerfTokens->vboRelocated);
257 
258     if (!TF_VERIFY(curRangeOwner == shared_from_this())) {
259         TF_CODING_ERROR("HdStVBOSimpleMemoryManager can't reassign ranges");
260         return;
261     }
262 
263     if (ranges.size() > 1) {
264         TF_CODING_ERROR("HdStVBOSimpleMemoryManager can't take multiple ranges");
265         return;
266     }
267     _SetRangeList(ranges);
268 
269     _SimpleBufferArrayRangeSharedPtr range = _GetRangeSharedPtr();
270 
271     if (!range) {
272         TF_CODING_ERROR("_SimpleBufferArrayRange expired unexpectedly.");
273         return;
274     }
275 
276     int numElements = range->GetNumElements();
277 
278     // Use blit work to record resource copy commands.
279     Hgi* hgi = _resourceRegistry->GetHgi();
280     HgiBlitCmds* blitCmds = _resourceRegistry->GetGlobalBlitCmds();
281     blitCmds->PushDebugGroup(__ARCH_PRETTY_FUNCTION__);
282 
283     TF_FOR_ALL (bresIt, GetResources()) {
284         HdStBufferResourceSharedPtr const &bres = bresIt->second;
285 
286         size_t bytesPerElement = HdDataSizeOfTupleType(bres->GetTupleType());
287         size_t bufferSize = bytesPerElement * numElements;
288 
289         HgiBufferHandle oldBuf = bres->GetHandle();
290         HgiBufferHandle newBuf;
291 
292         if(bufferSize > 0) {
293             HgiBufferDesc bufDesc;
294             bufDesc.byteSize = bufferSize;
295             bufDesc.usage = HgiBufferUsageUniform;
296             newBuf = hgi->CreateBuffer(bufDesc);
297         }
298 
299         // copy the range. There are three cases:
300         //
301         // 1. src length (capacity) == dst length (numElements)
302         //   Copy the entire range
303         //
304         // 2. src length < dst length
305         //   Enlarging the range. This typically happens when
306         //   applying quadrangulation/subdivision to populate
307         //   additional data at the end of source data.
308         //
309         // 3. src length > dst length
310         //   Shrinking the range. When the garbage collection
311         //   truncates ranges.
312         //
313         int oldSize = range->GetCapacity();
314         int newSize = range->GetNumElements();
315         size_t copySize = std::min(oldSize, newSize) * bytesPerElement;
316         if (copySize > 0 && oldBuf) {
317             HD_PERF_COUNTER_INCR(HdStPerfTokens->copyBufferGpuToGpu);
318 
319             HgiBufferGpuToGpuOp blitOp;
320             blitOp.gpuSourceBuffer = oldBuf;
321             blitOp.gpuDestinationBuffer = newBuf;
322             blitOp.byteSize = copySize;
323             blitCmds->CopyBufferGpuToGpu(blitOp);
324         }
325 
326         // delete old buffer
327         if (oldBuf) {
328             hgi->DestroyBuffer(&oldBuf);
329         }
330 
331         bres->SetAllocation(newBuf, bufferSize);
332     }
333 
334     blitCmds->PopDebugGroup();
335 
336     _capacity = numElements;
337     _needsReallocation = false;
338 
339     // increment version to rebuild dispatch buffers.
340     IncrementVersion();
341 }
342 
343 size_t
GetMaxNumElements() const344 HdStVBOSimpleMemoryManager::_SimpleBufferArray::GetMaxNumElements() const
345 {
346     static size_t vboMaxSize = TfGetEnvSetting(HD_MAX_VBO_SIZE);
347     return vboMaxSize / _maxBytesPerElement;
348 }
349 
350 void
_DeallocateResources()351 HdStVBOSimpleMemoryManager::_SimpleBufferArray::_DeallocateResources()
352 {
353     Hgi* hgi = _resourceRegistry->GetHgi();
354     TF_FOR_ALL (it, GetResources()) {
355         hgi->DestroyBuffer(&it->second->GetHandle());
356     }
357 }
358 
359 HdStBufferResourceSharedPtr
GetResource() const360 HdStVBOSimpleMemoryManager::_SimpleBufferArray::GetResource() const
361 {
362     HD_TRACE_FUNCTION();
363 
364     if (_resourceList.empty()) return HdStBufferResourceSharedPtr();
365 
366     if (TfDebug::IsEnabled(HD_SAFE_MODE)) {
367         // make sure this buffer array has only one resource.
368         HgiBufferHandle const& buffer =
369                 _resourceList.begin()->second->GetHandle();
370         TF_FOR_ALL (it, _resourceList) {
371             if (it->second->GetHandle() != buffer) {
372                 TF_CODING_ERROR("GetResource(void) called on"
373                                 "HdBufferArray having multiple GPU resources");
374             }
375         }
376     }
377 
378     // returns the first item
379     return _resourceList.begin()->second;
380 }
381 
382 HdStBufferResourceSharedPtr
GetResource(TfToken const & name)383 HdStVBOSimpleMemoryManager::_SimpleBufferArray::GetResource(TfToken const& name)
384 {
385     HD_TRACE_FUNCTION();
386 
387     // linear search.
388     // The number of buffer resources should be small (<10 or so).
389     for (HdStBufferResourceNamedList::iterator it = _resourceList.begin();
390          it != _resourceList.end(); ++it) {
391         if (it->first == name) return it->second;
392     }
393     return HdStBufferResourceSharedPtr();
394 }
395 
396 HdBufferSpecVector
GetBufferSpecs() const397 HdStVBOSimpleMemoryManager::_SimpleBufferArray::GetBufferSpecs() const
398 {
399     HdBufferSpecVector result;
400     result.reserve(_resourceList.size());
401     TF_FOR_ALL (it, _resourceList) {
402         result.emplace_back(it->first, it->second->GetTupleType());
403     }
404     return result;
405 }
406 
407 // ---------------------------------------------------------------------------
408 //  _SimpleBufferArrayRange
409 // ---------------------------------------------------------------------------
410 bool
IsAssigned() const411 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::IsAssigned() const
412 {
413     return (_bufferArray != nullptr);
414 }
415 
416 bool
IsImmutable() const417 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::IsImmutable() const
418 {
419     return (_bufferArray != nullptr)
420          && _bufferArray->IsImmutable();
421 }
422 
423 bool
RequiresStaging() const424 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::RequiresStaging() const
425 {
426     return false;
427 }
428 
429 void
CopyData(HdBufferSourceSharedPtr const & bufferSource)430 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::CopyData(
431     HdBufferSourceSharedPtr const &bufferSource)
432 {
433     HD_TRACE_FUNCTION();
434     HF_MALLOC_TAG_FUNCTION();
435 
436     if (!TF_VERIFY(_bufferArray)) return;
437 
438     int offset = 0;
439 
440     HdStBufferResourceSharedPtr VBO =
441         _bufferArray->GetResource(bufferSource->GetName());
442 
443     if (!VBO || !VBO->GetHandle()) {
444         TF_CODING_ERROR("VBO doesn't exist for %s",
445                         bufferSource->GetName().GetText());
446         return;
447     }
448 
449     size_t bytesPerElement = HdDataSizeOfTupleType(VBO->GetTupleType());
450     // overrun check. for graceful handling of erroneous assets,
451     // issue warning here and continue to copy for the valid range.
452     size_t dstSize = _numElements * bytesPerElement;
453     size_t srcSize =
454         bufferSource->GetNumElements() *
455         HdDataSizeOfTupleType(bufferSource->GetTupleType());
456     if (srcSize > dstSize) {
457         TF_WARN("%s: size %ld is larger than the range (%ld)",
458                 bufferSource->GetName().GetText(), srcSize, dstSize);
459         srcSize = dstSize;
460     }
461 
462     size_t vboOffset = bytesPerElement * offset;
463 
464     HD_PERF_COUNTER_INCR(HdStPerfTokens->copyBufferCpuToGpu);
465 
466     HgiBufferCpuToGpuOp blitOp;
467     blitOp.cpuSourceBuffer = bufferSource->GetData();
468     blitOp.gpuDestinationBuffer = VBO->GetHandle();
469 
470     blitOp.sourceByteOffset = 0;
471     blitOp.byteSize = srcSize;
472     blitOp.destinationByteOffset = vboOffset;
473 
474     HgiBlitCmds* blitCmds = GetResourceRegistry()->GetGlobalBlitCmds();
475     blitCmds->CopyBufferCpuToGpu(blitOp);
476 }
477 
478 VtValue
ReadData(TfToken const & name) const479 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::ReadData(TfToken const &name) const
480 {
481     HD_TRACE_FUNCTION();
482     HF_MALLOC_TAG_FUNCTION();
483 
484     if (!TF_VERIFY(_bufferArray)) return VtValue();
485 
486     HdStBufferResourceSharedPtr VBO = _bufferArray->GetResource(name);
487 
488     if (!VBO || (!VBO->GetHandle() && _numElements > 0)) {
489         TF_CODING_ERROR("VBO doesn't exist for %s", name.GetText());
490         return VtValue();
491     }
492 
493     return HdStGLUtils::ReadBuffer(VBO->GetHandle()->GetRawResource(),
494                                    VBO->GetTupleType(),
495                                  /*offset=*/0,
496                                  /*stride=*/0,  // not interleaved.
497                                  _numElements);
498 }
499 
500 size_t
GetMaxNumElements() const501 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::GetMaxNumElements() const
502 {
503     return _bufferArray->GetMaxNumElements();
504 }
505 
506 HdBufferArrayUsageHint
GetUsageHint() const507 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::GetUsageHint() const
508 {
509     if (!TF_VERIFY(_bufferArray)) {
510         return HdBufferArrayUsageHint();
511     }
512 
513     return _bufferArray->GetUsageHint();
514 }
515 
516 HdStBufferResourceSharedPtr
GetResource() const517 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::GetResource() const
518 {
519     if (!TF_VERIFY(_bufferArray)) return HdStBufferResourceSharedPtr();
520 
521     return _bufferArray->GetResource();
522 }
523 
524 HdStBufferResourceSharedPtr
GetResource(TfToken const & name)525 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::GetResource(TfToken const& name)
526 {
527     if (!TF_VERIFY(_bufferArray)) return HdStBufferResourceSharedPtr();
528     return _bufferArray->GetResource(name);
529 }
530 
531 HdStBufferResourceNamedList const&
GetResources() const532 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::GetResources() const
533 {
534     if (!TF_VERIFY(_bufferArray)) {
535         static HdStBufferResourceNamedList empty;
536         return empty;
537     }
538     return _bufferArray->GetResources();
539 }
540 
541 void
SetBufferArray(HdBufferArray * bufferArray)542 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::SetBufferArray(HdBufferArray *bufferArray)
543 {
544     _bufferArray = static_cast<_SimpleBufferArray *>(bufferArray);
545 }
546 
547 void
DebugDump(std::ostream & out) const548 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::DebugDump(std::ostream &out) const
549 {
550     out << "[SimpleBAR] numElements = " << _numElements
551         << "\n";
552 }
553 
554 const void *
_GetAggregation() const555 HdStVBOSimpleMemoryManager::_SimpleBufferArrayRange::_GetAggregation() const
556 {
557     return _bufferArray;
558 }
559 
560 PXR_NAMESPACE_CLOSE_SCOPE
561 
562