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